Expose strict brief rendering without implicit Git operations
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
This commit is contained in:
parent
200cbd0a61
commit
09803dcc31
9 changed files with 199 additions and 26 deletions
|
|
@ -2,18 +2,19 @@
|
|||
# Custodian Brief — state-hub
|
||||
|
||||
**Domain:** infotech
|
||||
**Last synced:** 2026-10-13 21:26 UTC
|
||||
**Last synced:** 2026-10-14 00:49 UTC
|
||||
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||
|
||||
## Active Workstreams
|
||||
|
||||
### Supply State Hub inputs that hub-core receipt tasks wait on
|
||||
Progress: 2/5 done | workplan_id: `0c68970e-295b-57fc-ac70-e472ee3a7bc4`
|
||||
Progress: 3/5 done | workplan_id: `0c68970e-295b-57fc-ac70-e472ee3a7bc4`
|
||||
|
||||
**Open tasks:**
|
||||
- ► A4 work/repo UI caller inventory and owner asks `e6ef3381`
|
||||
- · Named single reader and parity window `077797c8`
|
||||
- · Implement the inbox change feed `14c66646`
|
||||
- ! A4 work/repo UI caller inventory and owner asks `e6ef3381`
|
||||
*(wait: Owner map delivered and switch-plan requests sent 2026-10-13 to hub-core (5e2f749b), activity-core (13cff5ff), repo-manager (0c49c989), sbom-nexus (63ce4ead) and policy-nexus (d5dda84d); waiting for their read smokes and reversible switch plans.)*
|
||||
- ! Named single reader and parity window `077797c8`
|
||||
*(wait: The first reader is chosen once hub-core implements the accepted feed contract (HUB-WP-0011-T01) and the feed is deployed. Per D3, choose a reader that does not rely on broadcast delivery stamps.)*
|
||||
|
||||
### Route human gates and expose actionable owner returns
|
||||
Progress: 3/5 done | workplan_id: `2c4e96a3-ca0d-5003-b759-880976600ecc`
|
||||
|
|
@ -22,7 +23,7 @@ Progress: 3/5 done | workplan_id: `2c4e96a3-ca0d-5003-b759-880976600ecc`
|
|||
- ! Integrate deduplicated owner wakeups with the supported runner `cd73e265`
|
||||
*(wait: Memo 2 plan-only and submit trials wait for a live owner return; C-25 found zero findings across 13 public repos on 2026-10-04)*
|
||||
- ! Prepare approved receiving promotion and assess graph freshness (P6) `ec0acd06`
|
||||
*(wait: Memo 3 apply trials need a read-only Forgejo token for the Activity Core worker (draft CCR-2026-0034 in railiance-platform); then memo 4 schedule and 48 h freshness evidence)*
|
||||
*(wait: Memo 3 apply trials need a read-only Forgejo token for the Activity Core worker; then memo 4 schedule and 48 h freshness evidence. 2026-10-14: CCR-2026-0034 in railiance-platform is the keycape operator reader (applied), not this token; no Forgejo-read CCR for the Activity Core worker found.)*
|
||||
|
||||
### State Hub retirement strangler and disposition execution
|
||||
Progress: 6/9 done | workplan_id: `ed077b62-7048-5752-bf65-f90471f45854`
|
||||
|
|
|
|||
6
Makefile
6
Makefile
|
|
@ -515,3 +515,9 @@ register-from-classification-all:
|
|||
|
||||
clean:
|
||||
$(COMPOSE) down -v
|
||||
|
||||
# Explicit read-model brief rendering; never stages or commits files.
|
||||
.PHONY: brief-generate brief-check
|
||||
brief-generate brief-check:
|
||||
@test -n "$(BRIEF_REPO_PATH)" -a -n "$(BRIEF_REPO_SLUG)" || { echo 'Set BRIEF_REPO_PATH and BRIEF_REPO_SLUG'; exit 1; }
|
||||
$(PYTHON) scripts/generate_custodian_brief.py --repo-path "$(BRIEF_REPO_PATH)" --repo-slug "$(BRIEF_REPO_SLUG)" --api-base "$(STATE_HUB_API_BASE)" $(if $(filter brief-check,$@),--check,)
|
||||
|
|
|
|||
3
SCOPE.md
3
SCOPE.md
|
|
@ -14,6 +14,9 @@ repository files remain authoritative for repository-owned work records.
|
|||
- FastMCP server and tool reference
|
||||
- Observable dashboard
|
||||
- repo registration (classification-driven) and consistency synchronization
|
||||
- read-model orientation brief rendering without Git operations (`make brief-generate` /
|
||||
`brief-check`, explicit repo path and slug); rejects unavailable or malformed reads
|
||||
and preserves existing briefs on failure. See `docs/brief-generation.md`.
|
||||
- repo classification spine (14 market domains, `.repo-classification.yaml`)
|
||||
- task-flow engine and flow definitions
|
||||
- SBOM, contribution, capability, demand-weighted suggestion backlog, TPSC, DoI,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
| workplan | STATE-WP-ADHOC-2026-09-22 | finished | — | workplans/ADHOC-2026-09-22.md |
|
||||
| workplan | STATE-WP-ADHOC-2026-10-06 | finished | — | workplans/ADHOC-2026-10-06.md |
|
||||
| workplan | STATE-WP-ADHOC-2026-10-13 | finished | — | workplans/ADHOC-2026-10-13.md |
|
||||
| workplan | STATE-WP-ADHOC-2026-10-19 | finished | — | workplans/ADHOC-2026-10-19.md |
|
||||
| workplan | CUST-WP-0003 | finished | — | workplans/CUST-WP-0003-whi-kpi-card.md |
|
||||
| workplan | CUST-WP-0012 | finished | — | workplans/CUST-WP-0012-multi-user-onboarding.md |
|
||||
| workplan | CUST-WP-0038 | archived | — | workplans/CUST-WP-0038-state-hub-threephoenix-ha.md |
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
| workplan | STATE-WP-0095 | finished | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| workplan | STATE-WP-0096 | finished | — | workplans/STATE-WP-0096-private-decision-dispatch.md |
|
||||
| workplan | STATE-WP-0097 | finished | — | workplans/STATE-WP-0097-unlinked-decision-supersession.md |
|
||||
| workplan | STATE-WP-0098 | active | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| workplan | STATE-WP-0098 | blocked | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-ADHOC-2026-06-04-T01 | done | — | workplans/ADHOC-2026-06-04.md |
|
||||
| task | STATE-WP-ADHOC-2026-07-01-T01 | done | — | workplans/ADHOC-2026-07-01.md |
|
||||
| task | STATE-WP-ADHOC-2026-07-01-T02 | done | — | workplans/ADHOC-2026-07-01.md |
|
||||
|
|
@ -90,6 +91,7 @@
|
|||
| task | STATE-WP-ADHOC-2026-10-06-T02 | done | — | workplans/ADHOC-2026-10-06.md |
|
||||
| task | STATE-WP-ADHOC-2026-10-06-T03 | done | — | workplans/ADHOC-2026-10-06.md |
|
||||
| task | STATE-WP-ADHOC-2026-10-13-T01 | done | — | workplans/ADHOC-2026-10-13.md |
|
||||
| task | STATE-WP-ADHOC-2026-10-19-T01 | done | — | workplans/ADHOC-2026-10-19.md |
|
||||
| task | CUST-WP-0003-T01 | done | — | workplans/CUST-WP-0003-whi-kpi-card.md |
|
||||
| task | CUST-WP-0003-T02 | done | — | workplans/CUST-WP-0003-whi-kpi-card.md |
|
||||
| task | CUST-WP-0003-T03 | done | — | workplans/CUST-WP-0003-whi-kpi-card.md |
|
||||
|
|
@ -394,13 +396,19 @@
|
|||
| task | STATE-WP-0095-T02 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T03 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T04 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T05 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T06 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T07 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T08 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T09 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0095-T10 | done | — | workplans/STATE-WP-0095-historical-source-receiving.md |
|
||||
| task | STATE-WP-0096-T01 | done | — | workplans/STATE-WP-0096-private-decision-dispatch.md |
|
||||
| task | STATE-WP-0096-T02 | done | — | workplans/STATE-WP-0096-private-decision-dispatch.md |
|
||||
| task | STATE-WP-0096-T03 | done | — | workplans/STATE-WP-0096-private-decision-dispatch.md |
|
||||
| task | STATE-WP-0097-T01 | done | — | workplans/STATE-WP-0097-unlinked-decision-supersession.md |
|
||||
| task | STATE-WP-0097-T02 | done | — | workplans/STATE-WP-0097-unlinked-decision-supersession.md |
|
||||
| task | STATE-WP-0098-T01 | done | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T02 | progress | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T02 | wait | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T03 | done | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T04 | todo | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T05 | todo | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T04 | wait | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
| task | STATE-WP-0098-T05 | done | — | workplans/STATE-WP-0098-hub-core-receipt-inputs.md |
|
||||
|
|
|
|||
24
docs/brief-generation.md
Normal file
24
docs/brief-generation.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Render orientation briefs without Git operations
|
||||
|
||||
Use the existing State Hub renderer independently of consistency writeback:
|
||||
|
||||
```sh
|
||||
make brief-generate BRIEF_REPO_PATH=/path/to/repo BRIEF_REPO_SLUG=canonical-slug
|
||||
make brief-check BRIEF_REPO_PATH=/path/to/repo BRIEF_REPO_SLUG=canonical-slug
|
||||
```
|
||||
|
||||
The Python entry point is `scripts/generate_custodian_brief.py`; pass `--api-base`
|
||||
or set Make's `STATE_HUB_API_BASE` for another endpoint. It performs only API reads
|
||||
and writes `.custodian-brief.md` in generation mode. It never stages, commits,
|
||||
pushes, or writes to the hub. Check mode exits nonzero on drift without writing.
|
||||
Timestamp-only changes are ignored.
|
||||
|
||||
The brief describes the current hub read model, not the Git index. Sync authored
|
||||
work records before expecting their changes in the brief. API requests are not
|
||||
an atomic snapshot; inspect output if concurrent work is changing the hub.
|
||||
Missing or malformed endpoint responses stop generation before writing. Symlink
|
||||
targets are refused. Preserve any existing local changes before refreshing.
|
||||
|
||||
Review and stage the brief alongside the substantive source change that needs it.
|
||||
The generated-only commit guard remains in force. This command does not install
|
||||
an automatic brief hook or change existing consistency writeback defaults.
|
||||
|
|
@ -2847,8 +2847,9 @@ def _summarise_inbox_message(msg: dict[str, Any], *, now: datetime) -> dict[str,
|
|||
}
|
||||
|
||||
|
||||
def collect_inbox_hygiene(api_base: str, repo_slug: str) -> dict[str, Any]:
|
||||
messages = _api_get(
|
||||
def collect_inbox_hygiene(api_base: str, repo_slug: str, *, reader=None) -> dict[str, Any]:
|
||||
reader = reader or _api_get
|
||||
messages = reader(
|
||||
api_base,
|
||||
"/messages",
|
||||
{"to_agent": repo_slug, "unread_only": True, "limit": 100},
|
||||
|
|
@ -3392,7 +3393,19 @@ def _commit_working_memory_notes(repo_slug: str, repo_path: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> bool:
|
||||
def _strict_brief_get(api_base: str, path: str, params=None):
|
||||
"""Never turn an incomplete read-model snapshot into an empty brief."""
|
||||
value = _api_get(api_base, path, params, return_error=True)
|
||||
singular = path.startswith(("/repos/", "/topics/"))
|
||||
valid = (isinstance(value, dict) and bool(value) and "_error" not in value
|
||||
if singular else isinstance(value, list) and all(isinstance(row, dict) for row in value))
|
||||
if not valid:
|
||||
raise ValueError("brief read model unavailable or malformed at " + path)
|
||||
return value
|
||||
|
||||
|
||||
def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str, *,
|
||||
commit: bool = True, strict: bool = False, check: bool = False) -> bool:
|
||||
"""Generate .custodian-brief.md at the repo root and git-commit if changed.
|
||||
|
||||
The brief gives any agent — including subagents without MCP access and
|
||||
|
|
@ -3402,7 +3415,8 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
import datetime as _dt
|
||||
from datetime import timezone as _tz
|
||||
|
||||
repo = _api_get(api_base, f"/repos/{repo_slug}")
|
||||
reader = _strict_brief_get if strict else _api_get
|
||||
repo = reader(api_base, f"/repos/{repo_slug}")
|
||||
if not repo:
|
||||
return False
|
||||
|
||||
|
|
@ -3413,21 +3427,21 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
# lookup only for compatibility with older API responses that omitted it.
|
||||
workstreams: list[dict] = []
|
||||
for status in OPEN_WORKSTREAM_STATUSES:
|
||||
rows = _api_get(api_base, "/workplans", {"repo_id": repo_id, "status": status}) or []
|
||||
rows = reader(api_base, "/workplans", {"repo_id": repo_id, "status": status}) or []
|
||||
if isinstance(rows, list):
|
||||
workstreams.extend(rows)
|
||||
_ws_for_domain = workstreams if workstreams else []
|
||||
if not _ws_for_domain:
|
||||
all_ws = _api_get(api_base, "/workplans", {"repo_id": repo_id}) or []
|
||||
all_ws = reader(api_base, "/workplans", {"repo_id": repo_id}) or []
|
||||
_ws_for_domain = all_ws if isinstance(all_ws, list) else []
|
||||
if not domain_slug and _ws_for_domain:
|
||||
topic = _api_get(api_base, f"/topics/{_ws_for_domain[0].get('topic_id', '')}")
|
||||
topic = reader(api_base, f"/topics/{_ws_for_domain[0].get('topic_id', '')}")
|
||||
if topic:
|
||||
domain_slug = topic.get("domain_slug", "")
|
||||
|
||||
# Active repo goal (first active one if multiple)
|
||||
goal_text = ""
|
||||
goals = _api_get(api_base, "/repo-goals", {"repo_slug": repo_slug}) or []
|
||||
goals = reader(api_base, "/repo-goals", {"repo_slug": repo_slug}) or []
|
||||
if isinstance(goals, list):
|
||||
active_goals = [g for g in goals if g.get("status") == "active"]
|
||||
if active_goals:
|
||||
|
|
@ -3455,7 +3469,7 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
for ws in workstreams:
|
||||
ws_title = ws.get("title", ws.get("slug", "?"))
|
||||
ws_id = ws["id"]
|
||||
tasks = _api_get(api_base, "/tasks", {"workplan_id": ws_id}) or []
|
||||
tasks = reader(api_base, "/tasks", {"workplan_id": ws_id}) or []
|
||||
if not isinstance(tasks, list):
|
||||
tasks = []
|
||||
|
||||
|
|
@ -3502,7 +3516,8 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
else:
|
||||
lines += ["## Active Workstreams", "", "*(none — repo may need first-session setup)*"]
|
||||
|
||||
hygiene = collect_inbox_hygiene(api_base, repo_slug)
|
||||
hygiene = (collect_inbox_hygiene(api_base, repo_slug, reader=reader) if strict
|
||||
else collect_inbox_hygiene(api_base, repo_slug))
|
||||
if hygiene["stale_unread_count"] or hygiene["missing_thread"] or hygiene["work_requests_unpromoted"]:
|
||||
lines += ["", "## Inbox Hygiene", ""]
|
||||
if hygiene["stale_unread_count"]:
|
||||
|
|
@ -3539,7 +3554,7 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
counterpart = parse_blocked_on(blocked_on)
|
||||
if not counterpart:
|
||||
continue
|
||||
messages = _api_get(
|
||||
messages = reader(
|
||||
api_base,
|
||||
"/messages",
|
||||
{
|
||||
|
|
@ -3621,9 +3636,14 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
if _strip_ts(content) == _strip_ts(existing):
|
||||
return False # no meaningful change
|
||||
|
||||
if brief_path.is_symlink():
|
||||
raise ValueError("refuse redirected brief")
|
||||
if check:
|
||||
raise ValueError("brief differs from current read model")
|
||||
brief_path.write_text(content, encoding="utf-8")
|
||||
|
||||
# Commit the brief so remote workers can pull it
|
||||
if commit:
|
||||
_git_commit_writeback(
|
||||
repo_path,
|
||||
brief_path,
|
||||
|
|
|
|||
34
scripts/generate_custodian_brief.py
Normal file
34
scripts/generate_custodian_brief.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Render a current State Hub orientation brief without Git operations or hub writes."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from consistency_check import _write_custodian_brief
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--repo-path', type=Path, required=True)
|
||||
parser.add_argument('--repo-slug', required=True)
|
||||
parser.add_argument('--api-base', default='http://127.0.0.1:8000')
|
||||
parser.add_argument('--check', action='store_true')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if not re.fullmatch(r'[a-z0-9]+(?:-[a-z0-9]+)*', args.repo_slug):
|
||||
raise ValueError('canonical repository slug required')
|
||||
if not args.repo_path.is_dir():
|
||||
raise ValueError('repository path must exist')
|
||||
if (args.repo_path / '.custodian-brief.md').is_symlink():
|
||||
raise ValueError('refuse redirected brief')
|
||||
changed = _write_custodian_brief(args.api_base, args.repo_slug, str(args.repo_path),
|
||||
commit=False, strict=True, check=args.check)
|
||||
except (OSError, ValueError, KeyError, TypeError) as exc:
|
||||
parser.exit(1, f'Brief generation refused: {exc}\n')
|
||||
print(json.dumps({'repo': args.repo_slug, 'status': 'updated' if changed else 'current',
|
||||
'source': 'state-hub-read-model', 'git_operations': False}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
49
tests/test_brief_generation.py
Normal file
49
tests/test_brief_generation.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
"""Rendering-only briefs must preserve existing data on incomplete API snapshots."""
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
||||
import consistency_check as owner
|
||||
|
||||
|
||||
def responses(base, path, params=None, **kwargs):
|
||||
if path.startswith('/repos/'):
|
||||
return {'id': 'repo-1', 'domain_slug': 'infotech'}
|
||||
return []
|
||||
|
||||
|
||||
def test_render_does_not_commit_and_timestamp_only_retry_is_unchanged(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(owner, '_api_get', responses)
|
||||
commit = Mock(side_effect=AssertionError('Git operations forbidden'))
|
||||
monkeypatch.setattr(owner, '_git_commit_writeback', commit)
|
||||
assert owner._write_custodian_brief('unused', 'demo', str(tmp_path), commit=False, strict=True)
|
||||
before = (tmp_path / '.custodian-brief.md').read_bytes()
|
||||
assert not owner._write_custodian_brief('unused', 'demo', str(tmp_path), commit=False, strict=True)
|
||||
assert (tmp_path / '.custodian-brief.md').read_bytes() == before
|
||||
commit.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('failed_path', ['/repos/demo', '/workplans', '/repo-goals', '/messages', '/tasks'])
|
||||
@pytest.mark.parametrize('failure', [None, {'_error': 'offline'}, 'malformed'])
|
||||
def test_partial_snapshot_preserves_existing_brief(tmp_path, monkeypatch, failed_path, failure):
|
||||
def read(base, path, params=None, **kwargs):
|
||||
if path == failed_path:
|
||||
return failure
|
||||
if path == '/workplans':
|
||||
return [{'id': 'wp-1', 'title': 'Waiting'}]
|
||||
return responses(base, path, params, **kwargs)
|
||||
monkeypatch.setattr(owner, '_api_get', read)
|
||||
path = tmp_path / '.custodian-brief.md'; path.write_text('preserved')
|
||||
with pytest.raises(ValueError, match='unavailable or malformed'):
|
||||
owner._write_custodian_brief('unused', 'demo', str(tmp_path), commit=False, strict=True)
|
||||
assert path.read_text() == 'preserved'
|
||||
|
||||
|
||||
def test_check_has_no_write(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(owner, '_api_get', responses)
|
||||
path = tmp_path / '.custodian-brief.md'; path.write_text('preserved')
|
||||
with pytest.raises(ValueError, match='differs'):
|
||||
owner._write_custodian_brief('unused', 'demo', str(tmp_path), commit=False, strict=True, check=True)
|
||||
assert path.read_text() == 'preserved'
|
||||
28
workplans/ADHOC-2026-10-19.md
Normal file
28
workplans/ADHOC-2026-10-19.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
id: STATE-WP-ADHOC-2026-10-19
|
||||
type: workplan
|
||||
title: "Small source maintenance for 2026-10-19"
|
||||
domain: infotech
|
||||
repo: state-hub
|
||||
status: finished
|
||||
owner: codex
|
||||
created: "2026-10-19"
|
||||
updated: "2026-10-19"
|
||||
flavor: implementation
|
||||
depends_on: []
|
||||
---
|
||||
|
||||
## Render briefs without implicit Git commits
|
||||
|
||||
```task
|
||||
id: STATE-WP-ADHOC-2026-10-19-T01
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Expose the existing brief renderer through an explicit no-Git CLI and Make targets.
|
||||
Keep strict API failures from overwriting existing briefs, support read-only drift
|
||||
checks, document the read-model boundary in SCOPE. Supports CUST-WP-0079 provider
|
||||
adoption without generated-only guard bypass. Seventeen focused tests pass and
|
||||
Custodian live read-model check passes. Broader consistency validation recorded
|
||||
in the session progress receipt; no runtime deployment or scheduler activation.
|
||||
Loading…
Add table
Add a link
Reference in a new issue