Allow bound historical suffix tasks during normal sync
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a087d8-d22d-7d11-8169-bf22a729dff1
This commit is contained in:
parent
5ad49dd79e
commit
3db3e7e297
6 changed files with 241 additions and 6 deletions
59
docs/evidence/2026-09-09-natural-assistant-provenance.json
Normal file
59
docs/evidence/2026-09-09-natural-assistant-provenance.json
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"ok": true,
|
||||
"repo": "/home/worsch/repo-manager",
|
||||
"revision": "5ad49dd79e0ebc2f46bf5df68d2c1a079dc358c7",
|
||||
"cutover_commit": "329af60753b20896f30f098e8f75074652780032",
|
||||
"commit_count": 100,
|
||||
"assistants": {
|
||||
"claude-code": {
|
||||
"commit_count": 15,
|
||||
"models": [
|
||||
"opus"
|
||||
],
|
||||
"sessions": [
|
||||
"f2bff2d5-e9b2-4338-92ca-10282a927006",
|
||||
"f40c8f53-fb65-4980-9d29-bcdb3dd946f7"
|
||||
],
|
||||
"processes": [
|
||||
"2583210@bnt-lap001",
|
||||
"272883@bnt-lap001"
|
||||
]
|
||||
},
|
||||
"codex": {
|
||||
"commit_count": 84,
|
||||
"models": [
|
||||
"gpt-5.6-luna",
|
||||
"gpt-5.6-sol",
|
||||
"gpt-6-astra"
|
||||
],
|
||||
"sessions": [
|
||||
"01a023c0-a0a3-7c03-b395-5a0d2757214d",
|
||||
"01a028f0-a42f-7582-89a8-ebaad7343834",
|
||||
"01a02b22-9638-76d2-bbff-b7ea1770b118",
|
||||
"01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c",
|
||||
"01a070b5-4994-7271-bd8b-7c3dbcedec4b",
|
||||
"01a07ff8-19d0-7820-b4d0-1353833cb7fc"
|
||||
],
|
||||
"processes": []
|
||||
},
|
||||
"grok": {
|
||||
"commit_count": 1,
|
||||
"models": [],
|
||||
"sessions": [
|
||||
"01a04996-76e8-7f53-b971-1885cfbed436"
|
||||
],
|
||||
"processes": []
|
||||
}
|
||||
},
|
||||
"interleaved_sessions": [
|
||||
[
|
||||
"01a023c0-a0a3-7c03-b395-5a0d2757214d",
|
||||
"01a028f0-a42f-7582-89a8-ebaad7343834"
|
||||
]
|
||||
],
|
||||
"unattributed": {
|
||||
"before_cutover": 0,
|
||||
"after_cutover": 0,
|
||||
"known_automation": 0
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
id: RMGR-IN-0001
|
||||
kind: intake
|
||||
title: Validate assistant provenance on a natural multi-session window
|
||||
status: open
|
||||
status: closed
|
||||
origin: residual
|
||||
origin_ref: RMGR-WP-0009
|
||||
priority: low
|
||||
|
|
@ -15,8 +15,10 @@ description: When two coding-assistant sessions naturally commit to one reposito
|
|||
run rmgr assistant-provenance report and retain the non-secret interleaving evidence.
|
||||
Do not manufacture or infer a collision.
|
||||
created: '2026-08-21T20:09:23.942286+00:00'
|
||||
updated: '2026-08-21T20:09:23.942286+00:00'
|
||||
updated: '2026-09-09T20:31:04.003556Z'
|
||||
state_hub_intake_id: "01a025f3-40b0-7243-8002-dbe4609a6245"
|
||||
closed_at: '2026-09-09T20:31:04.003556Z'
|
||||
outcome: Natural multi-session evidence captured in docs/evidence/2026-09-09-natural-assistant-provenance.json.
|
||||
```
|
||||
|
||||
## RMGR-IN-0002 — Assign dormant shared prefixes before their next allocation
|
||||
|
|
@ -45,7 +47,7 @@ state_hub_intake_id: "01a02611-a663-7712-bd4b-e39c13e7bf20"
|
|||
id: RMGR-IN-0003
|
||||
kind: intake
|
||||
title: Scope normal sync identity validation to actionable records
|
||||
status: open
|
||||
status: closed
|
||||
origin: residual
|
||||
origin_ref: RMGR-WP-0005
|
||||
priority: low
|
||||
|
|
@ -57,6 +59,14 @@ description: Normal rmgr sync currently refuses net-kingdom because the archived
|
|||
visible as conformance evidence but do not block deterministic assignment or
|
||||
forge-derived reconciliation of unrelated canonical records.
|
||||
created: '2026-08-31T17:30:00+00:00'
|
||||
updated: '2026-08-31T17:30:00+00:00'
|
||||
updated: '2026-09-09T20:31:04.003556Z'
|
||||
state_hub_intake_id: "4f1d9836-e0a6-56ea-bf19-2587c153f338"
|
||||
closed_at: '2026-09-09T20:31:04.003556Z'
|
||||
outcome: Implemented and verified in RMGR-WP-0015.
|
||||
```
|
||||
|
||||
2026-09-09 resolutions: RMGR-IN-0001 is evidenced by
|
||||
`docs/evidence/2026-09-09-natural-assistant-provenance.json`: 100 existing
|
||||
commits, three assistants, and one naturally interleaved session pair. This
|
||||
is a Git sequence signal, not proof of concurrent writes or a collision.
|
||||
RMGR-IN-0003 is implemented and verified in RMGR-WP-0015.
|
||||
|
|
|
|||
|
|
@ -850,10 +850,37 @@ def ensure_missing_work_record_identifiers(
|
|||
repo_root = repo_root.resolve()
|
||||
namespace = namespace or load_fleet_namespace()
|
||||
identity = scan_record_identities(repo_root)
|
||||
if identity["invalid_identifiers"] or identity["identity_collisions"]:
|
||||
# Completed, already-bound suffix tasks are historical evidence, not
|
||||
# allocation candidates. Keep conformance reporting strict while allowing
|
||||
# ordinary registration to preserve those bindings. Local aliases still
|
||||
# require the explicit parent allowlist in the kind registry.
|
||||
historical_sources: set[str] = set()
|
||||
for path in iter_workplan_files(repo_root):
|
||||
parsed = parse_workplan_file(path, repo_root=repo_root)
|
||||
if parsed.status not in {"finished", "archived", "completed"} or not parsed.id:
|
||||
continue
|
||||
for number, task in enumerate(parsed.tasks, start=1):
|
||||
if task.status not in {"done", "cancel"} or not task.state_hub_task_id:
|
||||
continue
|
||||
if not re.fullmatch(re.escape(parsed.id) + r"-T[0-9]{2}[a-z]", task.id or ""):
|
||||
continue
|
||||
try:
|
||||
uuid.UUID(task.state_hub_task_id)
|
||||
except ValueError:
|
||||
continue
|
||||
historical_sources.add(f"{parsed.path}#task-block-{number}")
|
||||
invalid = [
|
||||
item for item in identity["invalid_identifiers"]
|
||||
if item["source"] not in historical_sources
|
||||
]
|
||||
historical = [
|
||||
item for item in identity["invalid_identifiers"]
|
||||
if item["source"] in historical_sources
|
||||
]
|
||||
if invalid or identity["identity_collisions"]:
|
||||
raise ValueError(
|
||||
"work-record identities are not safe to derive: "
|
||||
f"invalid={identity['invalid_identifiers']!r}, "
|
||||
f"invalid={invalid!r}, "
|
||||
f"collisions={identity['identity_collisions']!r}"
|
||||
)
|
||||
|
||||
|
|
@ -926,6 +953,7 @@ def ensure_missing_work_record_identifiers(
|
|||
"repo": repo_root.name,
|
||||
"namespace": namespace,
|
||||
"assignments": assignments,
|
||||
"historical_invalid_identifiers": historical,
|
||||
"files_changed": sorted(str(path.relative_to(repo_root)) for path in rendered),
|
||||
}
|
||||
|
||||
|
|
|
|||
63
tests/test_historical_sync_validation.py
Normal file
63
tests/test_historical_sync_validation.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from repo_manager.identifiers import ensure_missing_work_record_identifiers
|
||||
from repo_manager.record_identity import scan_record_identities
|
||||
|
||||
|
||||
def _sources(root: Path, *, status='completed', task_status='done',
|
||||
task_id='NK-WP-0003-T08a', binding='22222222-2222-4222-8222-222222222222'):
|
||||
archive = root / 'workplans' / 'archived' / 'NK-WP-0003.md'
|
||||
archive.parent.mkdir(parents=True)
|
||||
archive.write_text(
|
||||
f'---\nid: NK-WP-0003\nstatus: {status}\n'
|
||||
'state_hub_workstream_id: "11111111-1111-4111-8111-111111111111"\n---\n'
|
||||
f'\n```task\nid: {task_id}\nstatus: {task_status}\n'
|
||||
f'state_hub_task_id: "{binding}"\n```\n'
|
||||
)
|
||||
live = root / 'workplans' / 'NK-WP-0099.md'
|
||||
live.write_text('---\nid: NK-WP-0099\nstatus: active\n---\n'
|
||||
'\n```task\nid: NK-WP-0099-T01\nstatus: todo\n```\n')
|
||||
return archive, live
|
||||
|
||||
|
||||
@pytest.mark.parametrize('status', ['finished', 'archived', 'completed'])
|
||||
def test_historical_warning_preserves_source_and_allows_allocation(tmp_path, status):
|
||||
archive, live = _sources(tmp_path, status=status)
|
||||
before = {p: p.read_bytes() for p in (archive, live)}
|
||||
preview = ensure_missing_work_record_identifiers(tmp_path, execute=False)
|
||||
assert all(p.read_bytes() == content for p, content in before.items())
|
||||
result = ensure_missing_work_record_identifiers(tmp_path)
|
||||
assert result['assignments'] == preview['assignments']
|
||||
assert {x['record_id'] for x in result['assignments']} == {'NK-WP-0099', 'NK-WP-0099-T01'}
|
||||
assert archive.read_bytes() == before[archive]
|
||||
assert result['historical_invalid_identifiers'] == scan_record_identities(tmp_path)['invalid_identifiers']
|
||||
assert result['historical_invalid_identifiers'][0]['id'] == 'NK-WP-0003-T08a'
|
||||
assert not scan_record_identities(tmp_path)['ok']
|
||||
assert ensure_missing_work_record_identifiers(tmp_path)['assignments'] == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize('kwargs', [
|
||||
{'status': 'active'}, {'status': 'unknown'}, {'task_status': 'todo'},
|
||||
{'binding': ''}, {'binding': 'not-a-uuid'}, {'task_id': 'T08a'},
|
||||
{'task_id': 'T01'}, {'task_id': 'NK-WP-0099-T08a'},
|
||||
])
|
||||
def test_unsafe_history_refuses_before_any_allocation(tmp_path, kwargs):
|
||||
paths = _sources(tmp_path, **kwargs)
|
||||
before = {p: p.read_bytes() for p in paths}
|
||||
with pytest.raises(ValueError, match='identities are not safe'):
|
||||
ensure_missing_work_record_identifiers(tmp_path)
|
||||
assert all(p.read_bytes() == content for p, content in before.items())
|
||||
|
||||
|
||||
def test_historical_collision_still_blocks(tmp_path):
|
||||
archive, live = _sources(tmp_path)
|
||||
duplicate = archive.with_name('duplicate.md')
|
||||
duplicate.write_text(archive.read_text().replace(
|
||||
'22222222-2222-4222-8222-222222222222',
|
||||
'33333333-3333-4333-8333-333333333333'))
|
||||
before = live.read_bytes()
|
||||
with pytest.raises(ValueError, match='collisions='):
|
||||
ensure_missing_work_record_identifiers(tmp_path)
|
||||
assert live.read_bytes() == before
|
||||
27
workplans/ADHOC-2026-09-09.md
Normal file
27
workplans/ADHOC-2026-09-09.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
id: RMGR-WP-ADHOC-2026-09-09
|
||||
type: workplan
|
||||
title: "Validate natural assistant provenance"
|
||||
domain: infotech
|
||||
repo: repo-manager
|
||||
status: finished
|
||||
owner: codex
|
||||
created: "2026-09-09"
|
||||
updated: "2026-09-09"
|
||||
state_hub_workstream_id: "b4258afa-f8db-51a3-957d-f5aae3531117"
|
||||
---
|
||||
|
||||
## Capture the natural multi-session window
|
||||
|
||||
```task
|
||||
id: RMGR-WP-ADHOC-2026-09-09-T01
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "714af2ff-83ac-5dc6-a070-8eb5bf547090"
|
||||
```
|
||||
|
||||
Resolved RMGR-IN-0001 by running the existing assistant provenance report over
|
||||
100 commits ending at 5ad49dd79e0ebc2f46bf5df68d2c1a079dc358c7.
|
||||
Evidence: docs/evidence/2026-09-09-natural-assistant-provenance.json.
|
||||
The existing history contains three assistants and one naturally interleaved
|
||||
session pair. No collision or concurrent-write claim is inferred.
|
||||
48
workplans/RMGR-WP-0015-historical-sync-validation.md
Normal file
48
workplans/RMGR-WP-0015-historical-sync-validation.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
id: RMGR-WP-0015
|
||||
type: workplan
|
||||
title: "Preserve bound historical suffix tasks during normal sync"
|
||||
domain: infotech
|
||||
repo: repo-manager
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: infotech
|
||||
created: "2026-09-09"
|
||||
updated: "2026-09-09"
|
||||
state_hub_workstream_id: "20b82674-0459-5075-9fc9-8f95ccb940bb"
|
||||
---
|
||||
|
||||
Origin: RMGR-IN-0003. Net Kingdom has already repaired the original T08a
|
||||
source label, but the general ordinary-registration refusal remains.
|
||||
|
||||
## Scope the ordinary registration gate
|
||||
|
||||
```task
|
||||
id: RMGR-WP-0015-T01
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "b45b78c9-720b-50fe-86de-d263f8a9e813"
|
||||
```
|
||||
|
||||
Preserve existing UUIDs on parent-qualified TNN suffix tasks marked done/cancel
|
||||
in finished, archived, or legacy completed workplans. Report their invalid
|
||||
labels in the assignment receipt and retain strict conformance diagnostics.
|
||||
Do not allocate historical UUIDs, accept unknown local aliases, or bypass any
|
||||
identity collision. Active or unbound invalid records still fail before writes.
|
||||
|
||||
## Prove preservation and rejection boundaries
|
||||
|
||||
```task
|
||||
id: RMGR-WP-0015-T02
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "46f87efc-363b-5345-bea9-8bf8b99596a4"
|
||||
```
|
||||
|
||||
Cover unrelated canonical allocation, unchanged historical source bytes,
|
||||
idempotency, dry run, invalid/missing UUIDs, active records, local aliases,
|
||||
and collisions. Run the complete suite and a read-only Net Kingdom preflight.
|
||||
|
||||
Validation: 181 tests passed; targeted Ruff checks passed. Net Kingdom
|
||||
read-only identifier preflight passed with no assignments or file changes.
|
||||
RMGR-IN-0003 resolved; no implementation residuals.
|
||||
Loading…
Add table
Add a link
Reference in a new issue