fix: preserve source task sections and explicit empty notes
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
parent
ccb285fc40
commit
f767a1a245
4 changed files with 231 additions and 17 deletions
|
|
@ -17,6 +17,81 @@ from api.services import forge_credential as fc
|
|||
from api.services import forge_projection as fp
|
||||
|
||||
|
||||
def test_task_description_retains_nested_sections_and_matches_consistency(monkeypatch):
|
||||
monkeypatch.syspath_prepend(str(Path(__file__).parents[1] / "scripts"))
|
||||
from scripts.consistency_check import parse_task_blocks
|
||||
|
||||
body = '''## Factory admission
|
||||
|
||||
```task
|
||||
id: HFACT-WP-0001-T01
|
||||
status: progress
|
||||
description: Earlier inline summary.
|
||||
intervention_note: ""
|
||||
blocking_reason: ""
|
||||
```
|
||||
|
||||
---
|
||||
Current native admission requirements.
|
||||
|
||||
### Local proof
|
||||
|
||||
Retain the source evidence and limitations.
|
||||
|
||||
#### Remaining return
|
||||
|
||||
Exact operator binding remains open.
|
||||
---
|
||||
|
||||
## Next task
|
||||
|
||||
```task
|
||||
id: HFACT-WP-0001-T02
|
||||
status: wait
|
||||
```
|
||||
|
||||
Separate next-task description.
|
||||
'''
|
||||
derived = fp._parse_tasks(body, "HFACT-WP-0001")
|
||||
authoritative = parse_task_blocks(body)
|
||||
assert derived[0].description == authoritative[0]["description"]
|
||||
assert "### Local proof" in derived[0].description
|
||||
assert "#### Remaining return" in derived[0].description
|
||||
assert "Next task" not in derived[0].description
|
||||
assert not derived[0].description.startswith("---")
|
||||
assert derived[0].intervention_note == ""
|
||||
assert derived[0].blocking_reason == ""
|
||||
assert derived[1].description == authoritative[1]["description"]
|
||||
assert derived[1].intervention_note is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("heading", ["", "# Workplan\n", "## Task\n", "### Task\n"])
|
||||
def test_task_description_stops_before_next_task_block(heading, monkeypatch):
|
||||
monkeypatch.syspath_prepend(str(Path(__file__).parents[1] / "scripts"))
|
||||
from scripts.consistency_check import parse_task_blocks
|
||||
|
||||
body = heading + '''```task
|
||||
id: DEMO-WP-0001-T01
|
||||
status: todo
|
||||
```
|
||||
|
||||
First description.
|
||||
|
||||
```task
|
||||
id: DEMO-WP-0001-T02
|
||||
status: wait
|
||||
description: Inline fallback.
|
||||
intervention_note: null
|
||||
blocking_reason: null
|
||||
```
|
||||
'''
|
||||
derived = fp._parse_tasks(body, "DEMO-WP-0001")
|
||||
authoritative = parse_task_blocks(body)
|
||||
assert derived[0].description == authoritative[0]["description"] == "First description."
|
||||
assert derived[1].description == authoritative[1]["description"] == "Inline fallback."
|
||||
assert derived[1].intervention_note is derived[1].blocking_reason is None
|
||||
|
||||
|
||||
def test_identity_matches_the_fleet_derivation():
|
||||
"""Same namespace as ADR-007, so overlay and forge agree on identity."""
|
||||
assert fp.derived_record_uuid("CUST-WP-0067") == "16249302-2767-55df-aec0-d92c2751c225"
|
||||
|
|
|
|||
|
|
@ -146,3 +146,61 @@ async def test_reconcile_rejects_commit_mismatch_without_db_changes(
|
|||
)
|
||||
assert rows.status_code == 200
|
||||
assert rows.json() == []
|
||||
|
||||
|
||||
async def test_real_source_projection_converges_without_followup_task_patches(client, monkeypatch, tmp_path):
|
||||
"""Derive source through the real parser and persist it through the real API/DB."""
|
||||
await create_test_domain(client)
|
||||
await create_test_repo(client, slug="projection-test")
|
||||
from api.config import settings
|
||||
from api.routers import work_record_projection as route
|
||||
|
||||
monkeypatch.setattr(settings, "state_hub_instance_role", "primary")
|
||||
monkeypatch.setattr(settings, "state_hub_instance_label", "railiance01")
|
||||
root = tmp_path / "projection-test"
|
||||
(root / "workplans").mkdir(parents=True)
|
||||
path = root / "workplans/TEST-WP-0001.md"
|
||||
path.write_text('''---
|
||||
id: TEST-WP-0001
|
||||
type: workplan
|
||||
title: Factory test
|
||||
status: active
|
||||
---
|
||||
## Native admission
|
||||
|
||||
```task
|
||||
id: TEST-WP-0001-T01
|
||||
status: wait
|
||||
intervention_note: Review needed.
|
||||
blocking_reason: Waiting.
|
||||
needs_human: true
|
||||
```
|
||||
|
||||
Admission context.
|
||||
|
||||
### Proof
|
||||
|
||||
Retain full proof.
|
||||
''')
|
||||
commit = "a" * 40
|
||||
monkeypatch.setattr(route, "derive_from_forge", lambda slug: fp.derive_from_checkout(root, slug, commit))
|
||||
endpoint = "/repos/projection-test/work-record-projection/reconcile"
|
||||
first = await client.post(endpoint, json={"expected_commit": commit}, headers={"Idempotency-Key": "source:first"})
|
||||
assert first.status_code == 200, first.text
|
||||
task_id = fp.derived_record_uuid("TEST-WP-0001-T01")
|
||||
created = (await client.get(f"/tasks/{task_id}")).json()
|
||||
assert created["description"] == "Admission context.\n\n### Proof\n\nRetain full proof."
|
||||
assert created["needs_human"] is True
|
||||
|
||||
path.write_text(path.read_text().replace("Review needed.", '""').replace("Waiting.", '""').replace("needs_human: true", "needs_human: false"))
|
||||
commit = "b" * 40
|
||||
for key in ["source:updated", "source:independent-rederivation"]:
|
||||
result = await client.post(endpoint, json={"expected_commit": commit}, headers={"Idempotency-Key": key})
|
||||
assert result.status_code == 200, result.text
|
||||
assert result.json()["outcome"]["status"] in {"applied", "noop"}
|
||||
actual = (await client.get(f"/tasks/{task_id}")).json()
|
||||
assert actual["description"] == created["description"]
|
||||
assert actual["intervention_note"] == actual["blocking_reason"] == ""
|
||||
assert actual["needs_human"] is False
|
||||
if key == "source:independent-rederivation":
|
||||
assert result.json()["outcome"]["counts"]["updated_tasks"] == 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue