feat: ship DoC and DoR quality policies (STATE-WP-0076)
Add Intake Definition of Comprehension and work-item Definition of Ready as hub policies with dashboard edit surfaces, document the Definition family and DoX-Ok/Failed badge convention, and mark STATE-WP-0076 finished.
This commit is contained in:
parent
5a392385bc
commit
3008d9e2f0
13 changed files with 573 additions and 80 deletions
|
|
@ -16,6 +16,12 @@ Umbrella: [Work Records](/docs/work-records).
|
|||
Intakes **replace** the retired hub **suggestion** backlog for new work. See
|
||||
[Suggestions (legacy)](/docs/suggestions).
|
||||
|
||||
**Quality:** discovery comprehension is governed by
|
||||
[Definition of Comprehension (DoC)](/policy/intake-doc) (`DoC-Ok` /
|
||||
`DoC-Failed` / unassessed). DoC is independent of lifecycle `status` and is
|
||||
**not** implementation readiness ([DoR](/policy/work-item-dor)). Model:
|
||||
`docs/work-record-quality-gates.md`.
|
||||
|
||||
---
|
||||
|
||||
## Lifecycle
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ The Tasks page shows all tasks across every workplan and domain, with live
|
|||
filtering, a workstation distribution chart, and a waiting-tasks highlight
|
||||
section.
|
||||
|
||||
A **task** is a work-record kind: the smallest executable unit. Tasks always
|
||||
belong to a workplan. Umbrella: [Work Records](/docs/work-records).
|
||||
A **task** is a work-record kind: the smallest executable unit (work-item).
|
||||
Tasks always belong to a workplan. Umbrella: [Work Records](/docs/work-records).
|
||||
Implementation-readiness: [DoR](/policy/work-item-dor).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -76,8 +76,16 @@ Assessments do **not** invent extra lifecycle states. Outcomes are:
|
|||
A workplan may be `status=finished` without **DoD-Ok**, or `status=ready`
|
||||
without **DoR-Ok**. That is allowed: process can advance while quality debt
|
||||
stays visible. Policies live under `policies/` (same pattern as Repository DoI
|
||||
and Service DoM). **DoC** (intake) and **DoR** (task/workplan) are introduced
|
||||
by workplan `STATE-WP-0076`.
|
||||
and Service DoM):
|
||||
|
||||
| Policy | Dashboard | Rates |
|
||||
|--------|-----------|-------|
|
||||
| **DoC** | [Intake DoC](/policy/intake-doc) | Discovery quality (`intake`) |
|
||||
| **DoR** | [Work-item DoR](/policy/work-item-dor) | Implementation-readiness (`task`, `workplan`) |
|
||||
| **DoD** | [Workplan DoD](/policy/workstream-dod) | Completion quality (workplan) |
|
||||
|
||||
Full matrix and badge spelling: repo doc `docs/work-record-quality-gates.md`
|
||||
(STATE-WP-0076).
|
||||
|
||||
Discovery quality (intake) is **not** the same as implementation-readiness
|
||||
(task/workplan). Outside or sparse-context signals should be comprehended
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ The product term is **workplan**. **Workstream** remains a legacy compatibility
|
|||
name in some fields (notably frontmatter `state_hub_workstream_id`) and
|
||||
historical URLs; it refers to the same entity.
|
||||
|
||||
**Quality vs lifecycle:** `status=ready` or `status=finished` is process state.
|
||||
Implementation-readiness and completion quality are separate assessments —
|
||||
[DoR](/policy/work-item-dor) (`DoR-Ok` / `DoR-Failed`) and
|
||||
[DoD](/policy/workstream-dod) (`DoD-Ok` / `DoD-Failed`). A finished workplan
|
||||
without DoD-Ok is allowed quality debt. See
|
||||
`docs/work-record-quality-gates.md`.
|
||||
|
||||
---
|
||||
|
||||
## Stored lifecycle states
|
||||
|
|
|
|||
92
dashboard/src/policy/intake-doc.md
Normal file
92
dashboard/src/policy/intake-doc.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
title: Intake Definition of Comprehension (DoC)
|
||||
---
|
||||
|
||||
```js
|
||||
import {API} from "../components/config.js";
|
||||
```
|
||||
|
||||
```js
|
||||
import {marked} from "npm:marked";
|
||||
|
||||
const _resp = await fetch(`${API}/policy/intake-doc`);
|
||||
if (!_resp.ok) throw new Error(`Failed to load policy: ${_resp.status}`);
|
||||
const _policy = await _resp.json();
|
||||
```
|
||||
|
||||
```js
|
||||
let _content = _policy.content;
|
||||
let _editing = false;
|
||||
|
||||
const _root = display(html`<div></div>`);
|
||||
|
||||
async function _save(text) {
|
||||
const r = await fetch(`${API}/policy/intake-doc`, {
|
||||
method: "PUT",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({content: text}),
|
||||
});
|
||||
if (!r.ok) throw new Error(`Save failed: ${r.status}`);
|
||||
_content = text;
|
||||
}
|
||||
|
||||
function _toolbar(...nodes) {
|
||||
return html`<div style="display:flex;gap:0.5rem;margin-bottom:1rem">${nodes}</div>`;
|
||||
}
|
||||
|
||||
function _btn(label, primary = false) {
|
||||
return html`<button style="
|
||||
padding:0.35rem 0.9rem;border-radius:4px;cursor:pointer;font-size:13px;
|
||||
background:${primary ? "#1e293b" : "#f1f5f9"};
|
||||
color:${primary ? "#f8fafc" : "#1e293b"};
|
||||
border:1px solid ${primary ? "#1e293b" : "#cbd5e1"};
|
||||
">${label}</button>`;
|
||||
}
|
||||
|
||||
function _render() {
|
||||
_root.innerHTML = "";
|
||||
|
||||
if (_editing) {
|
||||
const area = html`<textarea style="
|
||||
width:100%;box-sizing:border-box;height:520px;
|
||||
font-family:ui-monospace,monospace;font-size:13px;line-height:1.6;
|
||||
padding:0.75rem;border:1px solid #cbd5e1;border-radius:4px;
|
||||
background:#f8fafc;color:#1e293b;resize:vertical;
|
||||
">${_content}</textarea>`;
|
||||
|
||||
const saveBtn = _btn("Save", true);
|
||||
const cancelBtn = _btn("Cancel");
|
||||
|
||||
saveBtn.onclick = async () => {
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.textContent = "Saving…";
|
||||
try {
|
||||
await _save(area.value);
|
||||
_editing = false;
|
||||
_render();
|
||||
} catch (e) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.textContent = "Save";
|
||||
alert(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
cancelBtn.onclick = () => { _editing = false; _render(); };
|
||||
|
||||
_root.append(_toolbar(saveBtn, cancelBtn), area);
|
||||
|
||||
} else {
|
||||
const editBtn = _btn("Edit");
|
||||
editBtn.onclick = () => { _editing = true; _render(); };
|
||||
|
||||
const body = html`<div style="
|
||||
max-width:720px;line-height:1.7;
|
||||
"></div>`;
|
||||
body.innerHTML = marked.parse(_content);
|
||||
|
||||
_root.append(_toolbar(editBtn), body);
|
||||
}
|
||||
}
|
||||
|
||||
_render();
|
||||
```
|
||||
92
dashboard/src/policy/work-item-dor.md
Normal file
92
dashboard/src/policy/work-item-dor.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
title: Definition of Ready (DoR)
|
||||
---
|
||||
|
||||
```js
|
||||
import {API} from "../components/config.js";
|
||||
```
|
||||
|
||||
```js
|
||||
import {marked} from "npm:marked";
|
||||
|
||||
const _resp = await fetch(`${API}/policy/work-item-dor`);
|
||||
if (!_resp.ok) throw new Error(`Failed to load policy: ${_resp.status}`);
|
||||
const _policy = await _resp.json();
|
||||
```
|
||||
|
||||
```js
|
||||
let _content = _policy.content;
|
||||
let _editing = false;
|
||||
|
||||
const _root = display(html`<div></div>`);
|
||||
|
||||
async function _save(text) {
|
||||
const r = await fetch(`${API}/policy/work-item-dor`, {
|
||||
method: "PUT",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify({content: text}),
|
||||
});
|
||||
if (!r.ok) throw new Error(`Save failed: ${r.status}`);
|
||||
_content = text;
|
||||
}
|
||||
|
||||
function _toolbar(...nodes) {
|
||||
return html`<div style="display:flex;gap:0.5rem;margin-bottom:1rem">${nodes}</div>`;
|
||||
}
|
||||
|
||||
function _btn(label, primary = false) {
|
||||
return html`<button style="
|
||||
padding:0.35rem 0.9rem;border-radius:4px;cursor:pointer;font-size:13px;
|
||||
background:${primary ? "#1e293b" : "#f1f5f9"};
|
||||
color:${primary ? "#f8fafc" : "#1e293b"};
|
||||
border:1px solid ${primary ? "#1e293b" : "#cbd5e1"};
|
||||
">${label}</button>`;
|
||||
}
|
||||
|
||||
function _render() {
|
||||
_root.innerHTML = "";
|
||||
|
||||
if (_editing) {
|
||||
const area = html`<textarea style="
|
||||
width:100%;box-sizing:border-box;height:520px;
|
||||
font-family:ui-monospace,monospace;font-size:13px;line-height:1.6;
|
||||
padding:0.75rem;border:1px solid #cbd5e1;border-radius:4px;
|
||||
background:#f8fafc;color:#1e293b;resize:vertical;
|
||||
">${_content}</textarea>`;
|
||||
|
||||
const saveBtn = _btn("Save", true);
|
||||
const cancelBtn = _btn("Cancel");
|
||||
|
||||
saveBtn.onclick = async () => {
|
||||
saveBtn.disabled = true;
|
||||
saveBtn.textContent = "Saving…";
|
||||
try {
|
||||
await _save(area.value);
|
||||
_editing = false;
|
||||
_render();
|
||||
} catch (e) {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.textContent = "Save";
|
||||
alert(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
cancelBtn.onclick = () => { _editing = false; _render(); };
|
||||
|
||||
_root.append(_toolbar(saveBtn, cancelBtn), area);
|
||||
|
||||
} else {
|
||||
const editBtn = _btn("Edit");
|
||||
editBtn.onclick = () => { _editing = true; _render(); };
|
||||
|
||||
const body = html`<div style="
|
||||
max-width:720px;line-height:1.7;
|
||||
"></div>`;
|
||||
body.innerHTML = marked.parse(_content);
|
||||
|
||||
_root.append(_toolbar(editBtn), body);
|
||||
}
|
||||
}
|
||||
|
||||
_render();
|
||||
```
|
||||
|
|
@ -49,7 +49,7 @@ convention used in the Custodian State Hub.
|
|||
|
||||
| Topic | What it covers |
|
||||
|-------|---------------|
|
||||
| [Work Records](/docs/work-records) | Umbrella term, kind registry, spine, lifecycles, legacy name map |
|
||||
| [Work Records](/docs/work-records) | Umbrella term, unit vs structure, spine, lifecycle vs DoX quality badges |
|
||||
| [State Hub](/docs/state-hub) | Why/how/what — Derived Data Store principle, orchestrator role, architecture diagram, design principles |
|
||||
| [TPSC](/docs/tpsc) | Third-Party Services Catalog — tpsc.yaml format, ingest, MCP tools |
|
||||
| [TPSC — GDPR Maturity](/docs/gdpr-maturity) | 7-level CNIL/IAPP scale, per-level guidance, key GDPR concepts |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue