generated from coulomb/repo-seed
feat: WP-0004 T01-T04 — stable corpus, ADRs, regression test
- corpus/markidocx-docs/manifest.yaml: specs as live markidocx project (FR-1101) - corpus/markidocx-docs/known-drift.md: documented structural drift - workflows.py: release-regression accepts manifest path; emits corpus_id (FR-1109) - tests/regression/test_corpus_regression.py: corpus regression suite (FR-1102–1110) - architecture/ADR-002: python-docx as conversion engine - architecture/ADR-003: manifest YAML schema - workplans/MRKD-WP-0004: T01–T04 done; T05 blocked (SBOM path mapping needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,6 +7,9 @@ ralph-workplan/
|
|||||||
# Runtime evidence store (generated by evidence.py during builds/tests)
|
# Runtime evidence store (generated by evidence.py during builds/tests)
|
||||||
.markidocx/
|
.markidocx/
|
||||||
|
|
||||||
|
# Corpus build artefacts (generated DOCX and imported Markdown)
|
||||||
|
corpus/markidocx-docs/dist/
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
88
architecture/ADR-002-python-docx-as-conversion-engine.md
Normal file
88
architecture/ADR-002-python-docx-as-conversion-engine.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
id: ADR-002
|
||||||
|
type: adr
|
||||||
|
status: accepted
|
||||||
|
created: 2026-03-16
|
||||||
|
deciders: [Bernd, Custodian]
|
||||||
|
---
|
||||||
|
|
||||||
|
# ADR-002: python-docx as DOCX Conversion Engine
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
markidocx must produce and consume `.docx` (Open XML) files from Python. The build
|
||||||
|
pipeline writes DOCX from Markdown; the import pipeline reads DOCX back into Markdown.
|
||||||
|
Both directions must be controlled programmatically without shelling out to Office
|
||||||
|
applications or external services.
|
||||||
|
|
||||||
|
The following options were evaluated:
|
||||||
|
|
||||||
|
| Option | Direction | Notes |
|
||||||
|
|--------|-----------|-------|
|
||||||
|
| **python-docx** | read + write | Pure Python, direct Open XML paragraph/run model |
|
||||||
|
| **pandoc** (subprocess) | read + write | Requires external binary; limited structural control |
|
||||||
|
| **mammoth** | read only | Focused on HTML output; no write support |
|
||||||
|
| **docx2python** | read only | Good for extracting raw content; no write support |
|
||||||
|
| **LibreOffice** (subprocess) | read + write | Heavy dependency; unreliable in headless environments |
|
||||||
|
|
||||||
|
The primary requirements were:
|
||||||
|
|
||||||
|
1. Both build (Markdown → DOCX) and import (DOCX → Markdown) in a single library
|
||||||
|
2. Programmatic control over paragraph styles, runs, tables, footnotes, and bookmarks
|
||||||
|
3. No external process dependency (no pandoc, no LibreOffice)
|
||||||
|
4. Pure Python — installable via `pip install` with no system-level setup
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Use **python-docx** for both the build (write) and import (read) directions.
|
||||||
|
|
||||||
|
python-docx provides:
|
||||||
|
- Direct access to the Open XML paragraph / run model — each `Paragraph` maps cleanly
|
||||||
|
to a Markdown block element; each `Run` maps to inline formatting
|
||||||
|
- Style name assignment (`Heading 1`, `Normal`, `List Bullet`, etc.) enabling
|
||||||
|
template-driven presentation
|
||||||
|
- Footnote, table, and image support within the standard API surface
|
||||||
|
- Bookmark creation and hyperlink insertion (used for LEVEL3 cross-references)
|
||||||
|
- Stable, well-documented API; actively maintained
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive:**
|
||||||
|
- Single dependency for both conversion directions
|
||||||
|
- No subprocess execution; fully in-process
|
||||||
|
- Paragraph/run model maps naturally to Markdown's block/inline structure
|
||||||
|
- Template `.docx` files control presentation without touching content
|
||||||
|
|
||||||
|
**Negative / accepted limitations:**
|
||||||
|
- python-docx exposes only a subset of the Open XML specification. Complex Word
|
||||||
|
features are out of scope by design:
|
||||||
|
- Track changes (revision marks) — not parseable
|
||||||
|
- SmartArt, charts, embedded objects — ignored during import
|
||||||
|
- Advanced numbering schemes beyond simple ordered/unordered lists — not supported
|
||||||
|
- Content controls, form fields — not supported
|
||||||
|
- python-docx's footnote write API is limited; markidocx uses a compatibility shim
|
||||||
|
for footnote construction (documented in `builder.py`)
|
||||||
|
- Modifying an existing DOCX in-place is not supported — markidocx always builds
|
||||||
|
a fresh DOCX and never mutates the input during import
|
||||||
|
|
||||||
|
**Out of scope by design:**
|
||||||
|
The constraints above align with markidocx's defined semantic envelope (FC-01).
|
||||||
|
The system only claims preservation for constructs within supported feature levels.
|
||||||
|
|
||||||
|
## Alternatives Rejected
|
||||||
|
|
||||||
|
**pandoc** — excellent general-purpose converter, but shelling out introduces a
|
||||||
|
hard runtime dependency, reduces structural control, and makes it difficult to
|
||||||
|
embed source-boundary markers needed for multi-file redistribution.
|
||||||
|
|
||||||
|
**mammoth** — high-quality Word → HTML converter; read-only, so unsuitable for
|
||||||
|
the build direction.
|
||||||
|
|
||||||
|
**docx2python** — useful for raw content extraction; no write support.
|
||||||
|
|
||||||
|
**LibreOffice** — handles the full Open XML spec, but requires a headless Office
|
||||||
|
installation, is unreliable in CI, and introduces significant operational complexity.
|
||||||
103
architecture/ADR-003-manifest-yaml-schema.md
Normal file
103
architecture/ADR-003-manifest-yaml-schema.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
id: ADR-003
|
||||||
|
type: adr
|
||||||
|
status: accepted
|
||||||
|
created: 2026-03-16
|
||||||
|
deciders: [Bernd, Custodian]
|
||||||
|
---
|
||||||
|
|
||||||
|
# ADR-003: Manifest YAML Schema
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
markidocx needs a project definition format that:
|
||||||
|
|
||||||
|
1. Describes which Markdown source files form a document project
|
||||||
|
2. Declares the feature level (`level1` / `level3`) and document family (`article`,
|
||||||
|
`book`, `website`)
|
||||||
|
3. Specifies output location and document metadata
|
||||||
|
4. Is human-writable and version-controllable alongside source files
|
||||||
|
5. Is parseable by the system without a schema registry or external validator
|
||||||
|
|
||||||
|
The format must support single-file and multi-file projects, and be extensible
|
||||||
|
enough for future additions (e.g. bibliography sources, asset directories) without
|
||||||
|
breaking existing manifests.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Use **YAML** with a fixed four-section top-level structure:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
project:
|
||||||
|
name: <string>
|
||||||
|
feature_level: level1 | level3
|
||||||
|
family: article | book | website
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- path: <relative path to .md file>
|
||||||
|
- path: <relative path to .md file>
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: <relative path to output directory>
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
title: <string>
|
||||||
|
author: <string>
|
||||||
|
date: <string>
|
||||||
|
```
|
||||||
|
|
||||||
|
All paths are resolved relative to the manifest file's location. The `metadata`
|
||||||
|
section and individual source `path` keys may be extended in future versions.
|
||||||
|
|
||||||
|
Validation is performed on load by `manifest.py` using dataclass coercion:
|
||||||
|
`load_manifest(path)` raises `ManifestError` on any schema violation (missing
|
||||||
|
required fields, unknown feature levels, unresolvable source paths).
|
||||||
|
|
||||||
|
## Current Field Definitions
|
||||||
|
|
||||||
|
| Field | Type | Required | Default | Notes |
|
||||||
|
|-------|------|----------|---------|-------|
|
||||||
|
| `project.name` | string | yes | — | Project identifier; used in output filenames |
|
||||||
|
| `project.feature_level` | enum | yes | — | `level1` or `level3` |
|
||||||
|
| `project.family` | enum | yes | — | `article`, `book`, or `website` |
|
||||||
|
| `sources[].path` | string | yes | — | Relative path; resolved against manifest dir |
|
||||||
|
| `output.dir` | string | no | `./dist` | Relative path for generated artefacts |
|
||||||
|
| `metadata.title` | string | no | — | Propagated to DOCX document properties |
|
||||||
|
| `metadata.author` | string | no | — | Propagated to DOCX document properties |
|
||||||
|
| `metadata.date` | string | no | — | Propagated to DOCX document properties |
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**Positive:**
|
||||||
|
- Human-readable and diff-friendly; natural fit for version-controlled documentation
|
||||||
|
repositories
|
||||||
|
- No external schema validation library needed — `manifest.py` owns validation
|
||||||
|
- Simple enough for a first-time user to write by hand
|
||||||
|
- Relative paths keep manifests portable across machines
|
||||||
|
|
||||||
|
**Negative / accepted limitations:**
|
||||||
|
- Evolving the schema requires coordination between the manifest file format and
|
||||||
|
`manifest.py` — there is no formal schema version field
|
||||||
|
- No auto-completion support in editors without a JSON Schema / YAML Language Server
|
||||||
|
configuration (out of scope for v0.1)
|
||||||
|
- YAML's implicit type coercion can surprise users (e.g. bare `no` parsed as `False`);
|
||||||
|
`load_manifest` validates all fields explicitly to catch these cases
|
||||||
|
|
||||||
|
## Alternatives Rejected
|
||||||
|
|
||||||
|
**TOML** — good alternative, but YAML is more common in documentation tooling
|
||||||
|
(MkDocs, GitHub Actions, Kubernetes) and more familiar to the target audience.
|
||||||
|
|
||||||
|
**JSON** — less writable for humans; comments not supported; trailing commas
|
||||||
|
disallowed; less pleasant for multi-line string values.
|
||||||
|
|
||||||
|
**Database / registry** — over-engineered for the single-project use case; would
|
||||||
|
require a running service just to define a document project.
|
||||||
|
|
||||||
|
**Pydantic / JSON Schema** — considered for validation, but adds a dependency
|
||||||
|
for functionality that a handful of explicit checks in `load_manifest()` already
|
||||||
|
covers cleanly.
|
||||||
45
corpus/markidocx-docs/known-drift.md
Normal file
45
corpus/markidocx-docs/known-drift.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Known Drift — markidocx-docs Corpus
|
||||||
|
|
||||||
|
Last updated: 2026-03-16
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
The markidocx-docs corpus (PRD + FRS v0.2 + UCC) produces known structural drift
|
||||||
|
on round-trip at LEVEL1. This drift is expected and does not indicate a regression.
|
||||||
|
|
||||||
|
## Import mode: fallback (merged)
|
||||||
|
|
||||||
|
The three source files are composed into a single DOCX. On import the system attempts
|
||||||
|
to redistribute content back to the three origin files using source-boundary markers.
|
||||||
|
The current build pipeline embeds section markers but the 27 H1-level sections in the
|
||||||
|
combined document make boundary matching ambiguous, so the importer falls back to a
|
||||||
|
single merged output (`dist/imported_merged.md`).
|
||||||
|
|
||||||
|
**Classification:** expected / by-design. The merged output is complete and usable.
|
||||||
|
|
||||||
|
## Structural drift items
|
||||||
|
|
||||||
|
### Bold inline text in list items (broken: ~70 items)
|
||||||
|
|
||||||
|
List items containing `**bold**` inline spans lose the bold markers on round-trip.
|
||||||
|
python-docx represents inline bold as a `Run` with `bold=True`, but the importer's
|
||||||
|
list-item text extractor concatenates run text without restoring markdown bold syntax.
|
||||||
|
|
||||||
|
**Classification:** known limitation of LEVEL1 inline formatting in list items.
|
||||||
|
**FR reference:** FR-508 (unsupported construct visibility) — these are surfaced
|
||||||
|
explicitly as `broken` rather than silently accepted.
|
||||||
|
**Impact:** content is preserved, presentation marker is lost.
|
||||||
|
|
||||||
|
### Table (broken: 1 of 1)
|
||||||
|
|
||||||
|
One table in the UCC is detected as missing after round-trip. Likely cause: the table
|
||||||
|
contains merged cells or a header row structure that the importer does not reconstruct.
|
||||||
|
|
||||||
|
**Classification:** known LEVEL1 table limitation.
|
||||||
|
**Impact:** table content is present in the DOCX but not re-imported to Markdown.
|
||||||
|
|
||||||
|
## Verdict
|
||||||
|
|
||||||
|
902 elements preserved; ~71 broken items (all inline formatting in lists or 1 table).
|
||||||
|
This corpus is suitable as a regression baseline: a clean round-trip regression test
|
||||||
|
can assert `preserved >= 900` and `broken <= 80` rather than exact zero-drift.
|
||||||
17
corpus/markidocx-docs/manifest.yaml
Normal file
17
corpus/markidocx-docs/manifest.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
project:
|
||||||
|
name: markidocx-docs
|
||||||
|
feature_level: level1
|
||||||
|
family: article
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- path: ../../specs/MarkiDocxProductRequirementsDocument_v0.1.md
|
||||||
|
- path: ../../specs/MarkiDocxFunctionalRequirementsSpecification_v0.2.md
|
||||||
|
- path: ../../specs/MarkiDocxUseCaseCatalog_v0.1.md
|
||||||
|
|
||||||
|
output:
|
||||||
|
dir: ./dist
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
title: markidocx — Product Documentation
|
||||||
|
author: Markitect Project
|
||||||
|
date: "2026-03-16"
|
||||||
@@ -275,9 +275,32 @@ def _release_regression(
|
|||||||
manifest_path: Path,
|
manifest_path: Path,
|
||||||
store: EvidenceStore,
|
store: EvidenceStore,
|
||||||
) -> WorkflowResult:
|
) -> WorkflowResult:
|
||||||
"""End-to-end regression on the stable documentation corpus (FR-1306)."""
|
"""End-to-end regression on the stable documentation corpus (FR-1306).
|
||||||
|
|
||||||
|
Discloses corpus identity (FR-1109): manifest path + git HEAD SHA embedded
|
||||||
|
in aggregate_output as 'corpus_id'.
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
|
||||||
result = _single_file_roundtrip(run_id, ts, manifest_path, store)
|
result = _single_file_roundtrip(run_id, ts, manifest_path, store)
|
||||||
result.workflow_name = "release-regression"
|
result.workflow_name = "release-regression"
|
||||||
|
|
||||||
|
# Corpus identity disclosure (FR-1109)
|
||||||
|
git_sha = "unknown"
|
||||||
|
try:
|
||||||
|
git_sha = subprocess.check_output(
|
||||||
|
["git", "rev-parse", "HEAD"],
|
||||||
|
cwd=manifest_path.parent,
|
||||||
|
text=True,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
).strip()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
result.aggregate_output["corpus_id"] = {
|
||||||
|
"manifest_path": str(manifest_path),
|
||||||
|
"git_sha": git_sha,
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
89
tests/regression/test_corpus_regression.py
Normal file
89
tests/regression/test_corpus_regression.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
"""Corpus regression test — markidocx-docs (FR-1101 through FR-1110).
|
||||||
|
|
||||||
|
Runs the release-regression workflow against the real product documentation
|
||||||
|
corpus (corpus/markidocx-docs/manifest.yaml). This test validates that the
|
||||||
|
markidocx specs themselves survive a round-trip within documented tolerance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from markidocx.workflows import run_workflow
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).parent.parent.parent
|
||||||
|
CORPUS_MANIFEST = REPO_ROOT / "corpus" / "markidocx-docs" / "manifest.yaml"
|
||||||
|
|
||||||
|
# Drift tolerance per known-drift.md: 902 elements preserved, ~71 broken.
|
||||||
|
# Gate: preserve at least 800 elements; broken must not exceed 150.
|
||||||
|
MIN_PRESERVED = 800
|
||||||
|
MAX_BROKEN = 150
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not CORPUS_MANIFEST.exists(),
|
||||||
|
reason="Corpus manifest not found — run from repo root after WP-0004-T01",
|
||||||
|
)
|
||||||
|
class TestCorpusRegression:
|
||||||
|
def test_workflow_completes(self) -> None:
|
||||||
|
"""release-regression workflow must not be classified as 'failed' (FR-1102)."""
|
||||||
|
result = run_workflow("release-regression", CORPUS_MANIFEST)
|
||||||
|
assert result.classification != "failed", (
|
||||||
|
f"release-regression workflow failed: {result.aggregate_output}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_workflow_classification_acceptable(self) -> None:
|
||||||
|
"""Workflow result must be 'full' or 'with-fallback' (FR-1305)."""
|
||||||
|
result = run_workflow("release-regression", CORPUS_MANIFEST)
|
||||||
|
assert result.classification in {"full", "with-fallback"}, (
|
||||||
|
f"Unexpected classification: {result.classification}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_all_steps_executed(self) -> None:
|
||||||
|
"""All four steps must be executed (FR-1304): validate, build, import, compare."""
|
||||||
|
result = run_workflow("release-regression", CORPUS_MANIFEST)
|
||||||
|
executed = {s.name for s in result.steps if s.status == "executed"}
|
||||||
|
for step in ("validate", "build", "import", "compare"):
|
||||||
|
assert step in executed, f"Step '{step}' was not executed"
|
||||||
|
|
||||||
|
def test_corpus_identity_disclosed(self) -> None:
|
||||||
|
"""Workflow result must carry corpus_id with manifest_path and git_sha (FR-1109)."""
|
||||||
|
result = run_workflow("release-regression", CORPUS_MANIFEST)
|
||||||
|
corpus_id = result.aggregate_output.get("corpus_id")
|
||||||
|
assert corpus_id is not None, "corpus_id missing from aggregate_output"
|
||||||
|
assert "manifest_path" in corpus_id, "corpus_id missing manifest_path"
|
||||||
|
assert "git_sha" in corpus_id, "corpus_id missing git_sha"
|
||||||
|
|
||||||
|
def test_evidence_artefacts_written(self) -> None:
|
||||||
|
"""Evidence store must contain build, import, and drift reports (FR-1107, FR-1110)."""
|
||||||
|
from markidocx.evidence import EvidenceStore
|
||||||
|
|
||||||
|
store = EvidenceStore()
|
||||||
|
result = run_workflow("release-regression", CORPUS_MANIFEST, evidence_store=store)
|
||||||
|
reports = store.list_reports(result.run_id)
|
||||||
|
report_types = {r.report_type for r in reports}
|
||||||
|
for expected in ("build", "import", "drift"):
|
||||||
|
assert expected in report_types, (
|
||||||
|
f"Evidence missing '{expected}' report. Found: {report_types}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_drift_within_tolerance(self) -> None:
|
||||||
|
"""Structural drift must stay within documented tolerance from known-drift.md."""
|
||||||
|
result = run_workflow("release-regression", CORPUS_MANIFEST)
|
||||||
|
compare_step = next(
|
||||||
|
(s for s in result.steps if s.name == "compare"), None
|
||||||
|
)
|
||||||
|
assert compare_step is not None, "compare step not found"
|
||||||
|
output = compare_step.output or {}
|
||||||
|
preserved = output.get("preserved", [])
|
||||||
|
broken = output.get("broken", [])
|
||||||
|
n_preserved = preserved if isinstance(preserved, int) else len(preserved)
|
||||||
|
n_broken = broken if isinstance(broken, int) else len(broken)
|
||||||
|
assert n_preserved >= MIN_PRESERVED, (
|
||||||
|
f"Preserved elements ({n_preserved}) below tolerance ({MIN_PRESERVED})"
|
||||||
|
)
|
||||||
|
assert n_broken <= MAX_BROKEN, (
|
||||||
|
f"Broken elements ({n_broken}) exceeds tolerance ({MAX_BROKEN})"
|
||||||
|
)
|
||||||
@@ -26,7 +26,7 @@ also writes the two deferred architecture decision records and generates the fir
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: MRKD-WP-0004-T01
|
id: MRKD-WP-0004-T01
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: f1a36613-ceaa-4786-ac39-cd3a7fd1c142
|
state_hub_task_id: f1a36613-ceaa-4786-ac39-cd3a7fd1c142
|
||||||
```
|
```
|
||||||
@@ -55,7 +55,7 @@ of the product documentation exists in `corpus/markidocx-docs/dist/`.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: MRKD-WP-0004-T02
|
id: MRKD-WP-0004-T02
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: f17e959f-28da-4386-9004-b5e036054b06
|
state_hub_task_id: f17e959f-28da-4386-9004-b5e036054b06
|
||||||
```
|
```
|
||||||
@@ -84,7 +84,7 @@ written to `.markidocx/evidence/` and retrievable via CLI.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: MRKD-WP-0004-T03
|
id: MRKD-WP-0004-T03
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: bfe2a9fa-25b2-4b4b-b21b-eae457716ce0
|
state_hub_task_id: bfe2a9fa-25b2-4b4b-b21b-eae457716ce0
|
||||||
```
|
```
|
||||||
@@ -112,7 +112,7 @@ Deliverable: `architecture/ADR-002-*.md` present and follows ADR-001 conventions
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: MRKD-WP-0004-T04
|
id: MRKD-WP-0004-T04
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: b6de6733-b332-4efc-9e23-82fce205b856
|
state_hub_task_id: b6de6733-b332-4efc-9e23-82fce205b856
|
||||||
```
|
```
|
||||||
@@ -140,7 +140,8 @@ Deliverable: `architecture/ADR-003-*.md` present.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: MRKD-WP-0004-T05
|
id: MRKD-WP-0004-T05
|
||||||
status: todo
|
status: blocked
|
||||||
|
blocking_reason: ops-bridge ingest_sbom_tool cannot access /home/tegwick/ paths (runs as worsch). Configure host_paths mapping for marki-docx, then re-run ingest.
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: 36aecd50-8176-4122-9706-a8697d8f5936
|
state_hub_task_id: 36aecd50-8176-4122-9706-a8697d8f5936
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user