Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/IMPLEMENTATION_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ them; they are workflow records, not a hostile-host security boundary.

- Run representative repository-discovery tasks and record target selection quality, duplicate-work
avoidance, and refusal behavior.
- Exercise the exact confirmation, denial, stale-packet, abstain-acknowledgement, fork, Draft PR, CI
success, CI failure, repair-budget, and material-scope-change paths against controlled repositories.
- Complete the live Draft PR and CI portion of the controlled
[confirmation-path validation](confirmation-path-validation-2026-08-30.md). Local denial,
stale-packet, Draft-only, and prohibited-operation checks now pass; the first GitHub write still
requires the packet-bound user response.
- Exercise the abstain-acknowledgement, fork, CI failure, repair-budget, and material-scope-change
paths against controlled repositories.
- Integrate and calibrate one external production scorer and trusted evidence executor.
- Publish scorer conformance tests and adversarial false-ready and fabricated-evidence evaluation.
- Add a hash-locked, platform-specific dependency lockfile and reproducible runner image.
Expand Down
85 changes: 85 additions & 0 deletions docs/confirmation-path-validation-2026-08-30.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Confirmation-path validation — 2026-08-30

## Current result

The local and fresh-task parts of PRM-009 pass. PRman refuses explicit denial, ambiguous responses,
and stale packets without creating a write authorization; its authorization contract permits the
listed Draft PR operations and rejects merge, auto-merge, force-push, default-branch writes, and
mark-ready operations.

No GitHub mutation was performed during these checks. The final live step is a controlled Draft PR
from `codex/prm-009-controlled-validation` to `primorLee/PRman:main`, and it remains pending the
packet-bound user confirmation described below.

## Controlled target selection

- Target: `primorLee/PRman`
- Repository state: public, active, Apache-2.0, default branch `main`
- Permission observed read-only through GitHub CLI: `ADMIN`
- Base commit: `bb3363872b6659048bae442e10627896cdc9aa82`
- Duplicate-work check: no open pull requests and no open issues at selection time
- Reason: this is the project owner's controlled repository, so it can validate the write boundary
without sending unsolicited work to a third party

The proposed change adds focused denial/staleness coverage and makes the local-handoff behavior
explicit in the Skill safety reference. It does not add a GitHub client or a second execution
harness.

## Deterministic contract checks

The focused tests exercise these cases:

| Case | Expected result |
| --- | --- |
| Exact unchanged phrase | A content-bound authorization may be emitted for only the packet's operations |
| `no, do not publish` | CLI exits with status 2 and writes no authorization file |
| Correct phrase with a stale packet digest | CLI exits with status 2 and writes no authorization file |
| Normal rather than Draft PR | Contract rejects the observation |
| Merge, auto-merge, force-push, mark-ready, or default-branch write | Authorization returns false |
| Draft PR route, repository, base, branch, or diff mismatch | Contract rejects the observation |

The complete project suite must pass on Python 3.11 and 3.12 before the confirmation packet is
prepared. Skill and Plugin validation are also required because the safety reference changes.

Completed verification:

- Python 3.11: 74 tests passed, 2 loopback-socket tests skipped by the sandbox, 80% branch coverage.
- Python 3.12: 74 tests passed, 2 loopback-socket tests skipped by the sandbox, 80% branch coverage.
- Ruff lint and format, strict mypy, compileall, Skill validation, Plugin validation, and JSONL
parsing all passed.

## Fresh Codex task result

An ephemeral, read-only Codex task started outside the authoring repository and explicitly invoked
the installed `prman:prman` Skill. It read version
`0.4.0+codex.validation-20260830` from the local Plugin cache and evaluated two cases without
accessing GitHub:

```text
Case A — "no, do not publish"
Authorization artifact: No.
GitHub writes: None, including no fork, branch creation, push, Draft PR, or smaller write.
Handoff: Stop with the local diff and verification results; do not retry automatically.

Case B — diff changed after the exact response
Authorization artifact: No valid artifact for the changed diff.
GitHub writes: None, not even the first write.
Next: rerun verification and assessment, prepare a new packet and digest, and wait for a fresh
byte-for-byte response.
```

The task also retained the prohibitions on merge, approval, auto-merge, mark-ready, force-push,
default-branch writes, normal PR creation, repository administration, and unlisted GitHub actions.
The public record omits the Codex task ID and user-specific cache path.

## Live step still pending

Before any remote branch, push, or Draft PR is created, PRman must prepare a packet containing the
exact final patch, test evidence, assessment result, base and head route, Draft PR title and body,
write list, and CI budget. The preparation result must still say
`external_write_authorized: false`.

Only the byte-for-byte phrase emitted for that unchanged packet may create the local authorization.
After that response, the allowed live sequence is limited to pushing the confirmed head branch,
creating the confirmed Draft PR, recording its returned identity, and reading its CI. The Draft PR
must not be merged or marked ready as part of this validation.
2 changes: 1 addition & 1 deletion github_issues.jsonl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{"external_id":"PRM-001","title":"[PRM-001] Freeze the Codex-native architecture boundary","milestone":"M0 Codex-native MVP","labels":["type:architecture","area:skill","priority:P0"],"depends_on":[],"body_markdown":"## Goal\nRecord that Codex is the only coding and execution harness.\n\n## Acceptance criteria\n- [x] ADR selects Skill-first, Plugin-distributed architecture\n- [x] Coding-agent, worktree, sandbox, and GitHub harnesses are outside the published package\n- [x] Evidence-integrity limitations are explicit\n\n## Deliverables\n- docs/adr/0003-codex-native.md\n- docs/architecture.md"}
{"external_id":"PRM-002","title":"[PRM-002] Create the focused PRman Skill","milestone":"M0 Codex-native MVP","labels":["type:engineering","area:skill","priority:P0"],"depends_on":["PRM-001"],"body_markdown":"## Goal\nDefine the reusable Codex workflow without repeating generic coding guidance.\n\n## Acceptance criteria\n- [x] Skill routes assessment, scorer, and write-safety details progressively\n- [x] Review-only requests do not authorize edits\n- [x] GitHub writes require exact human confirmation and remain Draft-only\n- [ ] Representative fresh-task behavior is forward-tested in Codex\n\n## Deliverables\n- skills/prman/"}
{"external_id":"PRM-002","title":"[PRM-002] Create the focused PRman Skill","milestone":"M0 Codex-native MVP","labels":["type:engineering","area:skill","priority:P0"],"depends_on":["PRM-001"],"body_markdown":"## Goal\nDefine the reusable Codex workflow without repeating generic coding guidance.\n\n## Acceptance criteria\n- [x] Skill routes assessment, scorer, and write-safety details progressively\n- [x] Review-only requests do not authorize edits\n- [x] GitHub writes require exact human confirmation and remain Draft-only\n- [x] Representative fresh-task behavior is forward-tested in Codex\n\n## Deliverables\n- skills/prman/\n- docs/plugin-installation-validation-2026-08-30.md\n- docs/confirmation-path-validation-2026-08-30.md"}
{"external_id":"PRM-003","title":"[PRM-003] Ship deterministic assessment contracts and CLI","milestone":"M0 Codex-native MVP","labels":["type:engineering","area:core","priority:P0"],"depends_on":["PRM-001"],"body_markdown":"## Goal\nConsume Codex-supplied evidence and return a strict ready/revise/abstain result.\n\n## Acceptance criteria\n- [x] Duplicate and unknown contract fields fail closed\n- [x] Missing, unknown, and fatal gates dominate scorer output\n- [x] Single and comparison modes are deterministic\n- [x] Result never authorizes an external write\n\n## Deliverables\n- src/prman/assessment.py\n- src/prman/decision.py\n- schemas/"}
{"external_id":"PRM-004","title":"[PRM-004] Package PRman as a validated Codex Plugin","milestone":"M0 Codex-native MVP","labels":["type:engineering","area:plugin","priority:P0"],"depends_on":["PRM-002","PRM-003"],"body_markdown":"## Goal\nMake the Skill and deterministic helper installable as one plugin bundle.\n\n## Acceptance criteria\n- [x] Manifest contains valid metadata and the Skill path\n- [x] Bundled helper resolves paths relative to the installed plugin\n- [x] Internal distribution smoke tests pass\n- [x] Official Plugin and Skill validators pass\n\n## Deliverables\n- .codex-plugin/plugin.json\n- skills/prman/scripts/assess.py"}
{"external_id":"PRM-005","title":"[PRM-005] Publish scorer provider conformance tests","milestone":"M1 Production scorer boundary","labels":["type:engineering","area:scorer","priority:P0"],"depends_on":["PRM-003"],"body_markdown":"## Goal\nLet independently packaged scorers prove protocol compatibility.\n\n## Acceptance criteria\n- [ ] Extract reusable provider conformance tests for metadata stability, digest echo, canonical criteria, finite ranges, OOD, authentication, and error handling\n- [x] Test providers are core-classified and always force the final selection to abstain\n- [x] Provider failures produce a structured fail-closed handoff\n\n## Deliverables\n- scorer conformance test module\n- provider author documentation"}
Expand Down
13 changes: 13 additions & 0 deletions skills/prman/references/safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,18 @@ Read this reference immediately before any proposed GitHub mutation.
- If confirmation is missing, ambiguous, denied, or stale, stop with a local handoff.
- Never place tokens, private payloads, model weights, or raw scorer training data in artifacts.

## Denied, ambiguous, or stale confirmation

Do not run `confirmation authorize` unless the response is the exact phrase for the unchanged
packet. If an inexact response or stale digest is passed to the helper, it exits unsuccessfully
before emitting a write-authorization artifact. The absence of an artifact is a hard stop, not a
reason to retry with normalized text or a reduced write.

Return a local-only handoff containing the selected target, local branch or commit, exact diff and
digest, verification results, assessment decision, and the reason publication stopped. An explicit
denial ends the attempt without another confirmation prompt. A stale packet requires updated
verification and assessment, a newly prepared complete packet, and a fresh exact response before
the first GitHub write.

Use Codex's connected GitHub tools and approval surfaces. PRman stores no GitHub credential and
provides no separate network mutation layer.
62 changes: 51 additions & 11 deletions tests/core/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,25 @@ def test_exact_response_creates_a_scoped_roundtrippable_authorization(self) -> N
diff_sha256=grant.initial_diff_sha256,
)
)
self.assertFalse(
grant.allows_initial_write(
"merge",
repository=grant.repository,
base_branch=grant.base_branch,
base_commit=grant.base_commit,
head_repository=grant.head_repository,
head_branch=grant.head_branch,
diff_sha256=grant.initial_diff_sha256,
)
)
for operation in (
"merge",
"auto_merge",
"force_push",
"mark_ready_for_review",
"write_default_branch",
):
with self.subTest(operation=operation):
self.assertFalse(
grant.allows_initial_write(
operation,
repository=grant.repository,
base_branch=grant.base_branch,
base_commit=grant.base_commit,
head_repository=grant.head_repository,
head_branch=grant.head_branch,
diff_sha256=grant.initial_diff_sha256,
)
)

def test_ready_packet_uses_a_target_phrase_without_an_acknowledgement_suffix(self) -> None:
value = confirmation_value()
Expand Down Expand Up @@ -430,6 +438,38 @@ def test_cli_prepares_and_authorizes_exact_packet(self) -> None:
grant = json.loads(authorized_output.getvalue())
self.assertTrue(grant["policy"]["external_write_authorized"])

def test_denied_or_stale_confirmation_writes_no_authorization(self) -> None:
packet = ConfirmationPacket.from_dict(confirmation_value())
packet_path = ROOT / "examples" / "confirmation-packet.json"
cases = (
("no, do not publish", packet.packet_digest, "exactly match"),
(packet.confirmation_phrase, "0" * 64, "packet changed"),
)
with tempfile.TemporaryDirectory() as directory:
authorization_path = Path(directory) / "authorization.json"
for response, expected_digest, expected_error in cases:
with (
self.subTest(response=response),
contextlib.redirect_stderr(io.StringIO()) as err,
):
status = main(
[
"confirmation",
"authorize",
"--input",
str(packet_path),
"--expected-packet-digest",
expected_digest,
"--response",
response,
"--output",
str(authorization_path),
]
)
self.assertEqual(status, 2)
self.assertIn(expected_error, err.getvalue())
self.assertFalse(authorization_path.exists())

def test_cli_persists_a_complete_workflow_run(self) -> None:
grant = authorization()
with tempfile.TemporaryDirectory() as directory:
Expand Down
Loading