feat(canon): add contribution-convention v0.1, contrib/ templates, and first UPR artifact
- canon/standards/contribution-convention_v0.1.md: master spec for BR/FR/EP/UPR artifact types, directory layout, frontmatter schema, ID schemes (EP-DOMAIN-NNN for extension points), status lifecycle, and relationship to State Hub - canon/standards/contrib-templates/: four template files (br, fr, ep, upr) - contrib/upstream-prs/2026-02-26--observablehq--framework--toc-sidebar-inject.md: first real UPR artifact — proposes injectTocTop() to Observable Framework Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
44
canon/standards/contrib-templates/br-template.md
Normal file
44
canon/standards/contrib-templates/br-template.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
id: br-YYYY-MM-DD--org--repo--slug
|
||||||
|
type: br
|
||||||
|
target_org: <github-org-or-owner>
|
||||||
|
target_repo: <repository-name>
|
||||||
|
title: "Short description of the bug"
|
||||||
|
status: draft
|
||||||
|
domain: custodian # change to your domain
|
||||||
|
related_workstream: null
|
||||||
|
state_hub_contribution_id: null
|
||||||
|
created: "YYYY-MM-DD"
|
||||||
|
updated: "YYYY-MM-DD"
|
||||||
|
upstream_version: "X.Y.Z"
|
||||||
|
upstream_issue_url: null
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bug: <short description>
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
- **Upstream project**: `<target_org>/<target_repo>` v`<upstream_version>`
|
||||||
|
- **Our usage context**: _which component or module triggered this_
|
||||||
|
|
||||||
|
## Reproduction Steps
|
||||||
|
|
||||||
|
1. …
|
||||||
|
2. …
|
||||||
|
3. …
|
||||||
|
|
||||||
|
## Expected Behaviour
|
||||||
|
|
||||||
|
_What should happen._
|
||||||
|
|
||||||
|
## Actual Behaviour
|
||||||
|
|
||||||
|
_What actually happens. Include error messages or stack traces._
|
||||||
|
|
||||||
|
## Workaround
|
||||||
|
|
||||||
|
_Any workaround currently in use, or "None"._
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
_Any additional context, links to related issues, or observations._
|
||||||
50
canon/standards/contrib-templates/ep-template.md
Normal file
50
canon/standards/contrib-templates/ep-template.md
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
id: EP-DOMAIN-NNN
|
||||||
|
type: ep
|
||||||
|
ep_id: EP-DOMAIN-NNN
|
||||||
|
target_org: <github-org-or-owner>
|
||||||
|
target_repo: <repository-name>
|
||||||
|
title: "Short description of the extension point"
|
||||||
|
status: draft
|
||||||
|
domain: custodian # change to your domain
|
||||||
|
related_workstream: null
|
||||||
|
state_hub_contribution_id: null
|
||||||
|
created: "YYYY-MM-DD"
|
||||||
|
updated: "YYYY-MM-DD"
|
||||||
|
location: "src/file.ts:42"
|
||||||
|
upstream_issue_url: null
|
||||||
|
---
|
||||||
|
|
||||||
|
# Extension Point Proposal: EP-DOMAIN-NNN
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
_One-paragraph description of the extension point: where in the upstream codebase
|
||||||
|
it would live, what it would enable, and who would benefit._
|
||||||
|
|
||||||
|
## Location
|
||||||
|
|
||||||
|
**File**: `<path/in/upstream/repo>`
|
||||||
|
**Line**: _approximate location where the hook/callback would be inserted_
|
||||||
|
|
||||||
|
## Proposed Interface
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// or Python, Rust, etc. — match the upstream project's language
|
||||||
|
interface ExtensionPoint {
|
||||||
|
// …
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
_Why this extension point is valuable. What use cases does it unlock?
|
||||||
|
Why should upstream accept it rather than keeping it local?_
|
||||||
|
|
||||||
|
## Implementation Sketch
|
||||||
|
|
||||||
|
_Brief notes on how upstream might implement this, if relevant._
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
_References to related upstream issues, PRs, or discussions._
|
||||||
40
canon/standards/contrib-templates/fr-template.md
Normal file
40
canon/standards/contrib-templates/fr-template.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
id: fr-YYYY-MM-DD--org--repo--slug
|
||||||
|
type: fr
|
||||||
|
target_org: <github-org-or-owner>
|
||||||
|
target_repo: <repository-name>
|
||||||
|
title: "Short description of the requested feature"
|
||||||
|
status: draft
|
||||||
|
domain: custodian # change to your domain
|
||||||
|
related_workstream: null
|
||||||
|
state_hub_contribution_id: null
|
||||||
|
created: "YYYY-MM-DD"
|
||||||
|
updated: "YYYY-MM-DD"
|
||||||
|
upstream_issue_url: null
|
||||||
|
---
|
||||||
|
|
||||||
|
# Feature Request: <short description>
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
_Why we need this feature. What problem does it solve? What becomes possible?_
|
||||||
|
|
||||||
|
## Proposed Interface / API
|
||||||
|
|
||||||
|
```
|
||||||
|
# Sketch of the proposed interface, function signature, or configuration option.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# How we would use this feature in our codebase once available.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
_What we currently do instead (workaround, local implementation, etc.)._
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
_Any additional context, links to related discussions, or upstream roadmap references._
|
||||||
62
canon/standards/contrib-templates/upr-template.md
Normal file
62
canon/standards/contrib-templates/upr-template.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
id: upr-YYYY-MM-DD--org--repo--slug
|
||||||
|
type: upr
|
||||||
|
target_org: <github-org-or-owner>
|
||||||
|
target_repo: <repository-name>
|
||||||
|
title: "Short description of the upstream PR"
|
||||||
|
status: draft
|
||||||
|
domain: custodian # change to your domain
|
||||||
|
related_workstream: null
|
||||||
|
state_hub_contribution_id: null
|
||||||
|
created: "YYYY-MM-DD"
|
||||||
|
updated: "YYYY-MM-DD"
|
||||||
|
local_component_path: "relative/path/in/this/repo/component.js"
|
||||||
|
target_upstream_file: "path/in/upstream/repo/component.js"
|
||||||
|
upstream_pr_url: null
|
||||||
|
---
|
||||||
|
|
||||||
|
# Upstream PR: <short description>
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
_What this PR adds or fixes in the upstream project._
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
_Why we want this upstream rather than keeping it as a local fork.
|
||||||
|
What maintenance burden does upstreaming remove?_
|
||||||
|
|
||||||
|
## Local Component
|
||||||
|
|
||||||
|
**Path in this repo**: `<local_component_path>`
|
||||||
|
**Origin story**: _How/when this component was created locally._
|
||||||
|
|
||||||
|
## Target Upstream Location
|
||||||
|
|
||||||
|
**Repo**: `<target_org>/<target_repo>`
|
||||||
|
**File**: `<target_upstream_file>`
|
||||||
|
|
||||||
|
## Draft PR Body
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
_Fill in the PR summary for the upstream project here._
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
_Explain why the upstream project would benefit._
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- …
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- [ ] Tests added
|
||||||
|
- [ ] Existing tests pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
_Any upstream discussion, review feedback, or open questions._
|
||||||
157
canon/standards/contribution-convention_v0.1.md
Normal file
157
canon/standards/contribution-convention_v0.1.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
---
|
||||||
|
id: canon-contrib-convention
|
||||||
|
type: standard
|
||||||
|
title: "Contribution Convention v0.1"
|
||||||
|
domain: custodian
|
||||||
|
status: active
|
||||||
|
version: "0.1"
|
||||||
|
created: "2026-02-28"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Contribution Convention v0.1
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This document defines the canonical convention for tracking upstream contributions
|
||||||
|
across all custodian-ecosystem repositories. A *contribution* is any intentional
|
||||||
|
engagement with an external upstream project: a bug report, feature request,
|
||||||
|
extension-point proposal, or pull request.
|
||||||
|
|
||||||
|
Contributions are tracked as Markdown artifacts with typed YAML frontmatter,
|
||||||
|
committed to the repository that authors them, and indexed in the State Hub DB.
|
||||||
|
This enables the Custodian to maintain a full audit trail of all upstream
|
||||||
|
engagement across all six project domains.
|
||||||
|
|
||||||
|
## Artifact Types
|
||||||
|
|
||||||
|
| Code | Name | Description |
|
||||||
|
|------|------|-------------|
|
||||||
|
| `br` | Bug Report | A defect reported to an upstream project |
|
||||||
|
| `fr` | Feature Request | A capability requested from an upstream project |
|
||||||
|
| `ep` | Extension-Point Proposal | A proposed extension point in an upstream project |
|
||||||
|
| `upr`| Upstream PR | A pull request submitted or planned for upstream |
|
||||||
|
|
||||||
|
## Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
<repo-root>/
|
||||||
|
contrib/
|
||||||
|
bug-reports/ # BR artifacts
|
||||||
|
feature-requests/ # FR artifacts
|
||||||
|
extension-points/ # EP artifacts
|
||||||
|
upstream-prs/ # UPR artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
Each artifact file follows the naming pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
<YYYY-MM-DD>--<org>--<repo>--<slug>.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- `contrib/bug-reports/2026-03-01--observablehq--framework--plot-tooltip-flicker.md`
|
||||||
|
- `contrib/upstream-prs/2026-02-26--observablehq--framework--toc-sidebar-inject.md`
|
||||||
|
- `contrib/extension-points/EP-CUST-002--anthropic--claude-code--hook-on-tool-result.md`
|
||||||
|
|
||||||
|
## Frontmatter Schema
|
||||||
|
|
||||||
|
All contribution artifacts share a common base frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
id: <type>-<YYYY-MM-DD>--<org>--<repo>--<slug> # unique string ID
|
||||||
|
type: br | fr | ep | upr # required
|
||||||
|
target_org: <github-org-or-owner> # required
|
||||||
|
target_repo: <repository-name> # required
|
||||||
|
title: "Human-readable title" # required
|
||||||
|
status: draft | submitted | acknowledged | accepted | rejected | merged | withdrawn
|
||||||
|
domain: custodian | railiance | markitect | ... # originating domain
|
||||||
|
related_workstream: <workstream-slug> # optional
|
||||||
|
state_hub_contribution_id: <uuid> # set once registered in State Hub
|
||||||
|
created: "YYYY-MM-DD"
|
||||||
|
updated: "YYYY-MM-DD"
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
### BR — Bug Report (additional fields)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
upstream_version: "1.2.3" # version where bug was observed
|
||||||
|
reproduction_steps: |
|
||||||
|
1. …
|
||||||
|
2. …
|
||||||
|
expected: "What should happen"
|
||||||
|
actual: "What actually happens"
|
||||||
|
workaround: "Any known workaround" # optional
|
||||||
|
upstream_issue_url: "https://…" # optional — link once filed
|
||||||
|
```
|
||||||
|
|
||||||
|
### FR — Feature Request (additional fields)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
motivation: "Why this feature is needed"
|
||||||
|
proposed_api: |
|
||||||
|
# Sketch of the proposed interface
|
||||||
|
alternatives: "What we do instead today"
|
||||||
|
upstream_issue_url: "https://…" # optional
|
||||||
|
```
|
||||||
|
|
||||||
|
### EP — Extension-Point Proposal (additional fields)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ep_id: EP-<DOMAIN>-NNN # e.g. EP-RAIL-007
|
||||||
|
location: "src/file.ts:42" # file:line in upstream where hook would live
|
||||||
|
proposed_interface: |
|
||||||
|
# Interface or callback signature sketch
|
||||||
|
rationale: "Why this extension point is valuable"
|
||||||
|
upstream_issue_url: "https://…" # optional
|
||||||
|
```
|
||||||
|
|
||||||
|
### UPR — Upstream PR (additional fields)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
local_component_path: "relative/path/in/this/repo"
|
||||||
|
target_upstream_file: "path/in/upstream/repo"
|
||||||
|
upstream_pr_url: "https://…" # set once submitted
|
||||||
|
draft_pr_body: |
|
||||||
|
## Summary
|
||||||
|
…
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
…
|
||||||
|
```
|
||||||
|
|
||||||
|
## ID Schemes
|
||||||
|
|
||||||
|
- **BR / FR / UPR**: `<type>-<YYYY-MM-DD>--<org>--<repo>--<slug>` — date-prefixed slug
|
||||||
|
- **EP**: `EP-<DOMAIN>-NNN` where `<DOMAIN>` is uppercase (CUST, RAIL, MARK, etc.)
|
||||||
|
and `NNN` is a zero-padded three-digit sequence per domain (EP-CUST-001,
|
||||||
|
EP-CUST-002, …). This scheme is an extension of the Railiance EP convention
|
||||||
|
defined in the staged-promotion-lifecycle workstream; this canon document is
|
||||||
|
the authoritative master spec.
|
||||||
|
|
||||||
|
## Status Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
draft → submitted → acknowledged → accepted → merged
|
||||||
|
↘ ↘
|
||||||
|
rejected withdrawn
|
||||||
|
```
|
||||||
|
|
||||||
|
- `draft`: artifact created locally, not yet sent upstream
|
||||||
|
- `submitted`: issue filed or PR opened upstream
|
||||||
|
- `acknowledged`: upstream team has responded
|
||||||
|
- `accepted`: upstream has agreed to the proposal
|
||||||
|
- `rejected`: upstream has declined
|
||||||
|
- `merged`: PR merged / fix released
|
||||||
|
- `withdrawn`: we decided not to pursue
|
||||||
|
|
||||||
|
## Relationship to State Hub
|
||||||
|
|
||||||
|
Once an artifact is registered via `register_contribution()` MCP tool or
|
||||||
|
`POST /contributions/` API, the State Hub assigns a UUID and returns it.
|
||||||
|
That UUID is written back into `state_hub_contribution_id` in the frontmatter.
|
||||||
|
|
||||||
|
The State Hub is a **read/cache layer** — the Markdown file is always the
|
||||||
|
authoritative source of truth. If the DB is reset, contributions can be
|
||||||
|
re-ingested from the files.
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
id: upr-2026-02-26--observablehq--framework--toc-sidebar-inject
|
||||||
|
type: upr
|
||||||
|
target_org: observablehq
|
||||||
|
target_repo: framework
|
||||||
|
title: "Add injectTocTop() utility for injecting elements into the TOC sidebar"
|
||||||
|
status: draft
|
||||||
|
domain: custodian
|
||||||
|
related_workstream: contribution-tracking-sbom
|
||||||
|
state_hub_contribution_id: null
|
||||||
|
created: "2026-02-26"
|
||||||
|
updated: "2026-02-28"
|
||||||
|
local_component_path: "state-hub/dashboard/src/components/toc-sidebar.js"
|
||||||
|
target_upstream_file: "src/client/toc.ts"
|
||||||
|
upstream_pr_url: null
|
||||||
|
---
|
||||||
|
|
||||||
|
# Upstream PR: Add injectTocTop() utility for injecting elements into the TOC sidebar
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Propose adding an `injectTocTop(id, element)` utility to Observable Framework
|
||||||
|
that allows dashboard pages to prepend arbitrary HTML elements at the top of the
|
||||||
|
automatically-generated table-of-contents sidebar. This enables KPI cards, live
|
||||||
|
status indicators, and other contextual widgets to live alongside the page's
|
||||||
|
section links.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
Observable Framework generates a TOC sidebar from headings automatically, but
|
||||||
|
provides no standard way to inject content into it from page JavaScript. As a
|
||||||
|
result, every project that wants sidebar widgets must implement its own DOM
|
||||||
|
manipulation workaround.
|
||||||
|
|
||||||
|
By upstreaming a small, focused utility, the community gets a stable API for
|
||||||
|
sidebar injection that doesn't depend on Observable Framework's internal DOM
|
||||||
|
structure.
|
||||||
|
|
||||||
|
## Local Component
|
||||||
|
|
||||||
|
**Path in this repo**: `state-hub/dashboard/src/components/toc-sidebar.js`
|
||||||
|
**Origin story**: Created 2026-02-26 during implementation of the Decisions KPI
|
||||||
|
dashboard (State Hub v0.2). The component provides two utilities:
|
||||||
|
- `injectTocTop(id, element)`: remove-then-prepend to the TOC aside element
|
||||||
|
- `withDocHelp(element, docPath)`: attaches a `?` button that opens a doc page
|
||||||
|
as an iframe overlay
|
||||||
|
|
||||||
|
## Target Upstream Location
|
||||||
|
|
||||||
|
**Repo**: `observablehq/framework`
|
||||||
|
**File**: `src/client/toc.ts` (or a new `src/client/toc-inject.ts`)
|
||||||
|
|
||||||
|
## Draft PR Body
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Add `injectTocTop(id, element)` — a small utility for injecting custom elements
|
||||||
|
at the top of the Observable Framework table-of-contents sidebar.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
Dashboard pages frequently need contextual widgets (KPI boxes, live status
|
||||||
|
indicators, navigation aids) that belong alongside the TOC, not in the page
|
||||||
|
content flow. Currently this requires each project to directly manipulate the
|
||||||
|
`#observablehq-toc` DOM element, coupling page code to an internal DOM detail.
|
||||||
|
|
||||||
|
`injectTocTop` provides a stable API for this pattern:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import {injectTocTop} from "observablehq:toc";
|
||||||
|
|
||||||
|
const myWidget = html`<div class="kpi-box">...</div>`;
|
||||||
|
injectTocTop("my-kpi-box", myWidget);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Add `injectTocTop(id: string, element: HTMLElement): void` to Framework's
|
||||||
|
client runtime
|
||||||
|
- Export from the `observablehq:toc` module (or equivalent public API)
|
||||||
|
- Add documentation page: "Customising the sidebar"
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- [ ] Unit test: `injectTocTop` prepends element to `#observablehq-toc aside`
|
||||||
|
- [ ] Unit test: calling again with same `id` removes old element before
|
||||||
|
inserting new one (idempotent)
|
||||||
|
- [ ] Integration test: injected element appears above TOC links in rendered page
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The local implementation (`toc-sidebar.js`) uses `querySelector('#observablehq-toc aside')`
|
||||||
|
— this should be replaced with a stable API reference once upstreamed.
|
||||||
|
- The `withDocHelp` utility in the same file is a separate feature; this PR
|
||||||
|
focuses solely on `injectTocTop`.
|
||||||
|
- Observable Framework issue tracker should be checked for any existing
|
||||||
|
discussion of sidebar customisation before opening a new issue.
|
||||||
Reference in New Issue
Block a user