- scripts/cleanup_stale_tasks.py: daily script that cancels open tasks in completed/archived workstreams; handles 307 redirects; emits a cleanup progress event summarising results - Makefile: add cleanup-stale target (also suitable for cron) - ADR-001: append Workstream Closure Protocol section — mandatory closure review before marking workstream completed, with task classification table (done/cancelled/carry-forward) and Closure Review file format - WP-0002 + WP-0005: append Closure Review sections documenting the 2026-03-02 cleanup run (26 stale DB rows cancelled — all were legacy pre-ADR-001 DB-first records; file status was already done) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
325 lines
12 KiB
Markdown
325 lines
12 KiB
Markdown
---
|
||
id: CUST-WP-0002
|
||
type: workplan
|
||
title: "State Hub v0.3 — Contribution Tracking & SBOM"
|
||
domain: custodian
|
||
status: completed
|
||
owner: custodian
|
||
topic_slug: custodian
|
||
state_hub_workstream_id: 2446400d-6d01-4679-a314-92af0601c608
|
||
created: "2026-02-26"
|
||
updated: "2026-02-28"
|
||
---
|
||
|
||
# State Hub v0.3 — Contribution Tracking & SBOM
|
||
|
||
## Summary
|
||
|
||
Establish the custodian as the central authority for cross-repo upstream
|
||
contribution management (bug reports, feature requests, extension points,
|
||
upstream PRs) and software supply-chain transparency (SBOM aggregation,
|
||
licence governance).
|
||
|
||
## Context
|
||
|
||
Three interconnected layers:
|
||
- **Layer 1 — Convention & Canon**: define contrib/ directory structure and
|
||
artifact types (BR, FR, EP, UPR) in markdown with typed YAML frontmatter.
|
||
- **Layer 2 — Schema & API**: DB tables for contributions, repos (deferred
|
||
to v0.5), and SBOM entries; FastAPI routers.
|
||
- **Layer 3 — MCP Tools & Dashboard**: tracking tools and Observable pages.
|
||
|
||
## Dependencies
|
||
|
||
- Depends on: `dynamic-domains-multi-repo` (v0.5, CUST-WP-0005).
|
||
Four tasks in this workplan (P2.2, P2.4, P3.2, P4.1) are reduced in
|
||
scope because v0.5 supersedes their original designs:
|
||
- `managed_repos` table → done by v0.5 P2.1
|
||
- `/repos/` API router → done by v0.5 P2.2
|
||
- `register_repo` MCP tool → done by v0.5 P3.1
|
||
- Base registration workflow → done by v0.5 P2.3
|
||
- state_hub_dep_id: 2033172d-2462-4253-acb7-cb64c7432480
|
||
|
||
## Phase 1 — Convention & Canon
|
||
|
||
### P1.1 — Write canon/standards/contribution-convention_v0.1.md
|
||
|
||
```task
|
||
id: CUST-WP-0002-T01
|
||
state_hub_task_id: c2d7df02-5f37-4c02-a418-fe7ab0050edc
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
Define the master contrib/ convention: directory layout, artifact types
|
||
(BR/FR/EP/UPR), frontmatter schema per type, ID schemes (EP-<DOMAIN>-NNN
|
||
for EPs; date-prefixed slug for others), status lifecycle, and
|
||
relationship to state-hub. Authoritative reference for other repos.
|
||
|
||
### P1.2 — Create contrib/ templates for BR, FR, EP, UPR
|
||
|
||
```task
|
||
id: CUST-WP-0002-T02
|
||
state_hub_task_id: 5ef89639-950c-4d57-9578-6d0211edc2df
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
Four Markdown template files under `canon/standards/contrib-templates/`:
|
||
`br-template.md`, `fr-template.md`, `ep-template.md`, `upr-template.md`.
|
||
Each has typed YAML frontmatter matching the schema in P1.1 plus a guided
|
||
prose skeleton.
|
||
|
||
### P1.3 — Store Observable Framework TOC sidebar UPR as first artifact
|
||
|
||
```task
|
||
id: CUST-WP-0002-T03
|
||
state_hub_task_id: e8251873-647c-4a4b-b283-298260b19c22
|
||
status: done
|
||
priority: medium
|
||
```
|
||
|
||
Create `contrib/upstream-prs/2026-02-26--observablehq--framework--toc-sidebar-inject.md`
|
||
using the UPR template. Captures: summary of the `injectTocTop` utility,
|
||
motivation, link to local component, target upstream file, draft PR body.
|
||
Status: draft.
|
||
|
||
### P1.4 — Align Railiance EP convention docs with canon master spec
|
||
|
||
```task
|
||
id: CUST-WP-0002-T04
|
||
state_hub_task_id: 1c982457-de52-4fc1-a439-3bf3120bb5b6
|
||
status: done
|
||
priority: low
|
||
```
|
||
|
||
The staged-promotion-lifecycle workstream defines EP-RAIL-NNN IDs and
|
||
inline doc markers. Review and update so the Railiance convention is a
|
||
proper instance of the custodian master spec, not a parallel one.
|
||
|
||
## Phase 2 — Schema & API
|
||
|
||
### P2.1 — Design and implement contributions DB table + migration
|
||
|
||
```task
|
||
id: CUST-WP-0002-T05
|
||
state_hub_task_id: c41d71bd-dc88-4201-bd9a-3f8a4eae4910
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
Add `contributions` table: id (UUID PK), type (enum: br|fr|ep|upr),
|
||
target_org, target_repo, slug, title, status (enum:
|
||
draft|submitted|acknowledged|accepted|rejected|merged|withdrawn),
|
||
body_path (relative path to .md file), related_topic_id (nullable FK),
|
||
related_workstream_id (nullable FK), submitted_at, resolved_at, notes,
|
||
created_at, updated_at. Alembic migration.
|
||
|
||
### P2.2 — sbom_entries table + migration (managed_repos deferred to v0.5)
|
||
|
||
```task
|
||
id: CUST-WP-0002-T06
|
||
state_hub_task_id: 28c9bd38-05d8-4466-bff3-3ba4e3957635
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
SCOPE REDUCED — managed_repos superseded by v0.5 P2.1.
|
||
|
||
Do not create `managed_repos` here. After v0.5 P2.1 lands, add
|
||
`sbom_source` (text) and `last_sbom_at` (timestamp) columns to the
|
||
existing `managed_repos` table via an additive migration.
|
||
|
||
Then create `sbom_entries` table: id (UUID PK), repo_id (FK
|
||
managed_repos), package_name, package_version, ecosystem, license_spdx
|
||
(nullable), is_direct (bool), is_dev (bool), snapshot_at (timestamp —
|
||
new ingest replaces old entries for that repo), created_at.
|
||
|
||
Prerequisite: v0.5 P2.1 (managed_repos table) must be complete.
|
||
|
||
### P2.3 — Implement /contributions/ router (CRUD + status patch)
|
||
|
||
```task
|
||
id: CUST-WP-0002-T07
|
||
state_hub_task_id: ee1cd17c-6dbd-4321-bb72-4499147c4837
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
FastAPI router: GET list (filter by type, status, target_repo), POST
|
||
create, GET by id, PATCH status (validates lifecycle transitions), soft
|
||
delete (sets status=withdrawn). Schemas: `ContributionCreate`,
|
||
`ContributionRead`, `ContributionStatusPatch`. Add
|
||
`contribution_counts` to `/state/summary`.
|
||
|
||
### P2.4 — Implement /sbom/ router (basic /repos/ endpoints implemented by v0.5)
|
||
|
||
```task
|
||
id: CUST-WP-0002-T08
|
||
state_hub_task_id: 25f7ab5c-69d8-41d7-a108-38380eb1f3a9
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
SCOPE REDUCED — /repos/ superseded by v0.5 P2.2.
|
||
|
||
This task covers the `/sbom/` router only:
|
||
- `POST /sbom/ingest` — accept repo_slug + list of package entries;
|
||
replace existing snapshot for that repo.
|
||
- `GET /sbom/` — aggregated view across all repos, grouped by license_spdx.
|
||
- `GET /sbom/{repo_slug}/` — single-repo SBOM.
|
||
- `GET /sbom/report/licences/` — counts and repo lists per SPDX identifier,
|
||
flagging copyleft (GPL/AGPL/LGPL).
|
||
- Add `licence_risk_count` to `/state/summary`.
|
||
|
||
Prerequisite: v0.5 P2.1 (managed_repos) and P2.2 (sbom_entries) must
|
||
be complete.
|
||
|
||
### P2.5 — Write make ingest-sbom tooling (pyproject + package.json parsers)
|
||
|
||
```task
|
||
id: CUST-WP-0002-T09
|
||
state_hub_task_id: edcf9b02-8177-4abc-b133-d303cc7ea19d
|
||
status: done
|
||
priority: medium
|
||
```
|
||
|
||
`scripts/ingest_sbom.py`: parses `uv.lock` (Python deps with licence
|
||
metadata), `package-lock.json` / `yarn.lock` (Node deps); outputs
|
||
normalised list; POSTs to `/sbom/ingest/`. Makefile target:
|
||
`make ingest-sbom REPO=<slug>`. Composable into `make seed`.
|
||
|
||
## Phase 3 — MCP Tools & Dashboard
|
||
|
||
### P3.1 — New MCP tools: register_contribution, update_contribution_status, get_contributions
|
||
|
||
```task
|
||
id: CUST-WP-0002-T10
|
||
state_hub_task_id: 51b2999b-a9c7-4871-8ef8-f5c927ac2454
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
- `register_contribution(type, target_org, target_repo, title, body_path,
|
||
related_workstream_id?, notes?)` → ContributionRead
|
||
- `update_contribution_status(contribution_id, status, notes?)` →
|
||
ContributionRead
|
||
- `get_contributions(type?, status?, target_repo?)` → list
|
||
- Add `state://contributions` resource.
|
||
|
||
### P3.2 — New MCP tools: ingest_sbom, get_licence_report (register_repo implemented by v0.5)
|
||
|
||
```task
|
||
id: CUST-WP-0002-T11
|
||
state_hub_task_id: 20c5c2ae-7758-42cc-885c-a886957e44c2
|
||
status: done
|
||
priority: high
|
||
```
|
||
|
||
SCOPE REDUCED — register_repo superseded by v0.5 P3.1.
|
||
|
||
- `ingest_sbom(repo_slug: str, lockfile_path: str)` — calls
|
||
`scripts/ingest_sbom.py`, POSTs result to `/sbom/ingest/`.
|
||
- `get_licence_report()` — returns licence groups with copyleft flag from
|
||
`/sbom/report/licences/`.
|
||
- Add resources: `state://sbom/aggregated`, `state://sbom/{repo_slug}`.
|
||
|
||
Prerequisite: v0.5 P3.1 (register_repo tool) and P2.4 (/sbom/ router)
|
||
must be complete.
|
||
|
||
### P3.3 — Dashboard: contributions.md page
|
||
|
||
```task
|
||
id: CUST-WP-0002-T12
|
||
state_hub_task_id: 1eb13411-b2da-4d0d-8fe2-e926d029c50f
|
||
status: done
|
||
priority: medium
|
||
```
|
||
|
||
New Observable Framework page. Layout: filter bar (type, status,
|
||
target_repo), status Kanban columns
|
||
(draft → submitted → acknowledged → accepted/rejected/merged), count KPIs
|
||
per type. Chart: contribution velocity over time (cumulative, same period
|
||
selector as decisions page). Apply `injectTocTop` for page-level KPI.
|
||
|
||
### P3.4 — Dashboard: sbom.md page
|
||
|
||
```task
|
||
id: CUST-WP-0002-T13
|
||
state_hub_task_id: 1267f313-1bf3-4059-8276-bfefcd9f6aed
|
||
status: done
|
||
priority: medium
|
||
```
|
||
|
||
New Observable Framework page. Aggregated dependency table: package,
|
||
version, licence, repos using it, direct/dev flags. Licence summary donut
|
||
chart (MIT vs Apache-2.0 vs GPL family vs other). Copyleft risk section
|
||
(red highlight for GPL/AGPL/LGPL in direct prod deps). Per-repo
|
||
drill-down accordion. Register in `observablehq.config.js`.
|
||
|
||
### P3.5 — Update index.md overview to surface contribution and SBOM health
|
||
|
||
```task
|
||
id: CUST-WP-0002-T14
|
||
state_hub_task_id: abab0022-d3a4-45ef-a7e1-e0e7c05d295f
|
||
status: done
|
||
priority: low
|
||
```
|
||
|
||
Add contribution counts KPI card (total, by type, any needing follow-up).
|
||
Add licence risk indicator (green if no copyleft in direct prod deps, red
|
||
otherwise). Uses `/state/summary` fields added in P2.3 and P2.4.
|
||
|
||
## Phase 4 — Repo Integration Tooling
|
||
|
||
### P4.1 — Add sbom_source prompt + ingest-sbom step to registration workflow (base workflow by v0.5)
|
||
|
||
```task
|
||
id: CUST-WP-0002-T15
|
||
state_hub_task_id: 47987720-23db-4465-861b-1f93bb1bb391
|
||
status: done
|
||
priority: low
|
||
```
|
||
|
||
SCOPE REDUCED — base registration workflow superseded by v0.5 P2.3.
|
||
|
||
SBOM-specific additions to the workflow:
|
||
1. Extend the `add-repo` command to prompt for `ecosystem` and
|
||
`sbom_source` (path or URL to lockfile).
|
||
2. Optionally run `make ingest-sbom REPO=<slug>` automatically at end of
|
||
registration.
|
||
3. Update `project_claude_md.template` to document the contrib/ convention,
|
||
SBOM MCP tools, and `make ingest-sbom` target.
|
||
|
||
Prerequisite: v0.5 P2.3 (updated registration workflow) must be complete.
|
||
|
||
---
|
||
|
||
## Closure Review — 2026-03-02
|
||
|
||
**Outcome:** All 15 tasks completed. No carry-forwards. No dropped tasks.
|
||
|
||
**Context:** This workplan was created DB-first on 2026-02-28, before ADR-001 was formalised. The workplan file correctly recorded all tasks as `status: done`, but the DB rows were never synced from the file — they remained in their initial `todo` state in the database. The daily stale-task cleanup script (`scripts/cleanup_stale_tasks.py`) detected these 15 stale DB rows and cancelled them on 2026-03-02. No actual work was lost: all deliverables in Phase 1–4 were shipped as part of State Hub v0.3.
|
||
|
||
### Completed (DB updated at delivery time; file status = done)
|
||
|
||
- CUST-WP-0002-T01 — Write canon/standards/contribution-convention_v0.1.md
|
||
- CUST-WP-0002-T02 — Create contrib/ templates for BR, FR, EP, UPR
|
||
- CUST-WP-0002-T03 — Store Observable Framework TOC sidebar UPR as first artifact
|
||
- CUST-WP-0002-T04 — Align Railiance EP convention docs with canon master spec
|
||
- CUST-WP-0002-T05 — Design and implement contributions DB table + migration
|
||
- CUST-WP-0002-T06 — sbom_entries table + migration
|
||
- CUST-WP-0002-T07 — Implement /contributions/ router (CRUD + status patch)
|
||
- CUST-WP-0002-T08 — Implement /sbom/ router
|
||
- CUST-WP-0002-T09 — Write make ingest-sbom tooling
|
||
- CUST-WP-0002-T10 — New MCP tools: register_contribution, update_contribution_status, get_contributions
|
||
- CUST-WP-0002-T11 — New MCP tools: ingest_sbom, get_licence_report
|
||
- CUST-WP-0002-T12 — Dashboard: contributions.md page
|
||
- CUST-WP-0002-T13 — Dashboard: sbom.md page
|
||
- CUST-WP-0002-T14 — Update index.md overview to surface contribution and SBOM health
|
||
- CUST-WP-0002-T15 — Add sbom_source prompt + ingest-sbom step to registration workflow
|
||
|
||
### Cancelled (DB records only — legacy stale rows, not real cancellations)
|
||
|
||
All 15 DB task rows were cancelled by the cleanup script. The workplan file was authoritative; the DB rows were artefacts of the pre-ADR-001 DB-first creation pattern. This does not reflect a change in work outcome.
|