Skip to content

Instrument Phase 2 and Phase 3 boundaries with clock-authored phase events (#1853) - #1961

Merged
The01Geek merged 12 commits into
mainfrom
issue-1853-phase-2-and-phase-3-carry-no-event
Aug 25, 2026
Merged

Instrument Phase 2 and Phase 3 boundaries with clock-authored phase events (#1853)#1961
The01Geek merged 12 commits into
mainfrom
issue-1853-phase-2-and-phase-3-carry-no-event

Conversation

@prflow-implementer

@prflow-implementer prflow-implementer Bot commented Aug 25, 2026

Copy link
Copy Markdown

Summary

  • Phase 2 and Phase 3 dominate a long implement run's wall clock, but neither wrote a clock-authored timing record, so an expensive run's interior could not be reconstructed from disk. This adds a cheap, best-effort event subcommand to scripts/verification-flight.py and calls it at each Phase 2/3 boundary.
  • The subcommand generalizes the existing single-flight clock-authored event shape rather than adding a second recording idiom, and it is non-blocking by contract: it always exits 0, breadcrumbing a failed write instead of failing the run.

Changes

scripts/verification-flight.py: New event <name> [--payload <json>] [--log-dir <dir>] subcommand. It appends one {"event": <name>, "recorded_at": <iso>} record — timestamped from the helper's own _now()/_iso() clock, never the caller — to an append-only JSONL log at .prflow/logs/phase-events/phase-events.jsonl (opened with O_WRONLY|O_APPEND|O_CREAT, directory created best-effort). An optional --payload object merges extra keys in, but the reserved event and recorded_at keys can never be shadowed by a payload key. A malformed or non-object --payload is dropped with a stderr breadcrumb and the base event is still recorded. Any OSError on the write path emits a stderr breadcrumb and returns EXIT_OK.

Phase 2 prose (skills/implement/phases/phase-2-implement.md, skills/implement/phases/phase-2-sweeps-quality.md): each durability-checkpoint boundary (where scripts/phase2-durability-checkpoint.sh already runs) now also appends a phase2-checkpoint event, invoked with the vendored literal as the command's leading token.

Phase 3 prose (skills/implement/phases/phase-3-review.md, skills/implement/phases/phase-3-fix-loop.md, skills/review-and-fix/references/shadow-review.md): phase3-simplify-start / phase3-simplify-end around /simplify, phase3-reviewers-dispatch / phase3-reviewers-return around the Phase 3.3 review-and-fix dispatch and its return, and phase3-shadow-entry at shadow-review entry.

.gitignore: ignores /.prflow/logs/phase-events/ (placed after the !/.prflow/logs/ negation so the per-run event log is never committed).

docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md: new "Phase-boundary event instrumentation (issue #1853)" subsection documenting the subcommand, its non-blocking contract, the instrumented boundaries, and scope.

Scope: no new agent-volunteered duration field is added and no existing telemetry field changes meaning. The helper is already granted with a wildcard over its arguments (Bash(.prflow/vendor/prflow/scripts/verification-flight.py:*)), so the new subcommand adds no entry to lib/capability-profiles.json.

Resolves

Resolves #1853

Test Plan

  • TestPhaseEventAppend in lib/test/test_verification_flight.py (added in this PR) covers the event subcommand: a clock-authored record with exactly {event, recorded_at} (DEVFLOW_FLIGHT_NOW-pinned timestamp via vf._iso); append-only accumulation across successive events; the default log location under <cwd>/.prflow/logs/phase-events/; an optional payload merged with the reserved event/recorded_at keys protected from a spoofing payload; a non-object payload and an unparseable payload each breadcrumbing while still recording the base event; and a failed write (log dir under a regular file) that exits EXIT_OK and breadcrumbs. The phase-prose additions are agent-executed prompt prose with no wording pin (per the repository's recorded decision on such prose) and carry no automated coverage.
  • scripts/verification-flight.py is owned by the harness-python-guards module: lib/test/run-module.sh harness-python-guards.

Visual Changes

N/A

Breaking Changes

None

Prompt-surface size

Derived at e9cdbd97ef09550f44ef6d818d145ab4653f04d4 against merge-base 3a139ee22b3067a7f5025dfb5cc6065adc2547b6 (origin/main). Covered: tracked *.md under skills/, agents/, .prflow/prompt-extensions/.

Path Before After Δ bytes Δ %
skills/implement/phases/phase-2-implement.md 43,279 43,515 +236 +0.5%
skills/implement/phases/phase-2-sweeps-quality.md 46,400 46,636 +236 +0.5%
skills/implement/phases/phase-3-review.md 34,576 34,960 +384 +1.1%
skills/review-and-fix/references/shadow-review.md 61,165 61,346 +181 +0.3%
skills/implement/phases/phase-3-fix-loop.md 52,907 53,325 +418 +0.8%
Whole covered surface 2,132,444 2,133,899 +1,455 +0.1%

Generated via /prflow:implement (v2.34.22, claude-opus-4-8, low)

- test the non-object --payload branch (pr-test-analyzer, Important)
- make the default-location test behavioral (chdir + assert file lands)
- re-ignore .prflow/logs/phase-events/ runtime dir (final-pass)
Two reviewers flagged the comment overclaiming whole-line write atomicity;
state only O_APPEND's real guarantee (atomic EOF positioning).
…ndle for #1853

verification-flight.py is not granted in the read-only review profile (AC6
forbids adding it), so the phase3-reviewers-dispatch/return events must not
live in skills/review/ (breaks the #363/#540 review-head-grant pins). Move
them to skills/implement/phases/phase-3-fix-loop.md (implement profile).
Keep the phase3-shadow-entry event; shorten its lead and tighten unpinned
prose to fit under 61750 bytes without touching any pinned literal.
@prflow-implementer
prflow-implementer Bot marked this pull request as ready for review August 25, 2026 19:35
@prflow-implementer

Copy link
Copy Markdown
Author

/prflow:review

@prflow-reviewer prflow-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE with notes (clean, self-contained phase-event instrumentation; one internal-doc file-path inaccuracy and a payload-breadcrumb edge to tidy — none blocking at the critical threshold)

Issue Compliance

Reviewed against issue #1853: Instrument Phase 2 and Phase 3 boundaries with clock-authored phase events — criteria from the linked issue/workpad; scope unchanged. The shipped change satisfies the issue intent: a clock-authored event subcommand on scripts/verification-flight.py (timestamp from the helper's own _now()/_iso(), never the caller), an append-only JSONL log under .prflow/logs/phase-events/, an always-exit-0 non-blocking contract with a stderr breadcrumb on failure, and instrumentation calls at the Phase 2 durability-checkpoint boundaries and the Phase 3 /simplify, reviewer-dispatch/return, and shadow-entry boundaries. CI for the reviewed head (12ef71f6a2160ae89cd13df98d1220733e1b5ad0) reports lib + python tests: success and lint (shellcheck + actionlint + ruff): success, including the python-pool and monolith shards that carry TestPhaseEventAppend and the harness-python guards.

Verification Checklist Results

Verified via the Phase 3 specialist agents rather than a separate lite/agent checklist (focused, single-mechanism diff): 0 failed, 0 inconclusive. Core claims confirmed against HEAD — payload reserved-key protection (event/recorded_at never shadowed), O_APPEND|O_CREAT append semantics, OSError breadcrumb-and-exit-0 failed-write arm, and the .gitignore entry ordered after the !/.prflow/logs/ negation.

Code Review Findings

🟡 Suggestion / Minor

  1. docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md (the new "Phase-boundary event instrumentation" subsection, "instrumented boundaries" bullet) attributes the phase3-reviewers-dispatch / phase3-reviewers-return events to skills/review/phases/phase-3-agents.md "around reviewer dispatch", but that file is untouched by this PR — the two events are actually added in skills/implement/phases/phase-3-fix-loop.md, wrapping the review-and-fix loop invocation (not the review engine's reviewer fan-out). The writing-skills evidence marker on the issue workpad repeats the same wrong file and omits the file actually edited. This is a genuine diff-added documented inaccuracy, but it is behavior-inert internal-doc prose: docs/internal/ is rm -rf'd from the vendor slice (vendor-slice.sh:106) so no consumer reads it, and no tool parses its prose to decide behavior — so under the Phase 4.1.5 behavior-inert prose cap it is deterministically capped to Suggestion and does not drive a REJECT at the critical threshold. It should nonetheless be fixed (change the path to skills/implement/phases/phase-3-fix-loop.md and reword "around reviewer dispatch" to "around the review-and-fix loop invocation"), and the workpad evidence marker corrected to list the file actually touched. (raised by 3/4 agents)
  2. scripts/verification-flight.py cmd_event: a well-formed JSON null payload (--payload null) is silently dropped with no breadcrumb — json.loads("null") returns None, which is excluded by the elif payload is not None: guard that exists to avoid double-breadcrumbing the parse-error path. Both the docstring and the overview say a "non-object" payload is dropped with a stderr breadcrumb; null is a non-object value, so this one edge contradicts the stated contract. Impact is nil in practice (base event still recorded, exit 0, and no first-party caller passes --payload), but a distinct parse-error sentinel (so a genuine null reaches the non-object breadcrumb arm) — or an explicit contract carve-out for null — would resolve the code-vs-contract gap. (raised by 2/4 agents)
  3. Test-coverage gaps (all optional): the O_APPEND concurrent-append offset claim (verification-flight.py:249-251) is unpinned — every existing test passes sequentially and would stay green under a truncating open; a JSON null / scalar (42, "str") payload branch, an empty ("") and empty-object ({}) payload, a valid-falsy payload value ({"n":0}/{"b":false} — the repo's off-switch bug class), and the event-missing-name argparse SystemExit are unexercised. The phase-prose additions correctly carry no coverage (agent-executed prompt prose, per the repo's recorded decision). (raised by 1/4 agents)

over-grade annotation: finding 1 flagged shape 2 (behavior-inert internal-doc prose) — cap applied, capped to Suggestion.
truthfulness sweep: finding 1 is a diff-added documented inaccuracy but is governed by the Phase 4.1.5 behavior-inert prose cap (both inertness limbs established: not tool-read for behavior; pruned from the vendor slice so no external consumer), so it is filed capped at Suggestion rather than routed into the self-contradicting-diff carve-out.
intra-diff contradiction scan: no contradiction found.

Verdict Criteria

No verification-checklist FAIL/INCONCLUSIVE; no finding at or above the configured critical threshold. All findings are Suggestion-level (the internal-doc inaccuracy is behavior-inert-capped; the remainder are diagnostic/test-coverage). → APPROVE with notes.

Recommended before merge: correct the internal-doc file attribution (finding 1); the payload-null and test-coverage items are optional.

@prflow-implementer

Copy link
Copy Markdown
Author

/prflow:review

@prflow-reviewer prflow-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE with notes

PR #1961 (issue #1853) adds a clock-authored event subcommand to scripts/verification-flight.py and instruments the Phase 2/3 boundaries. The functional change is correct, well-tested, and satisfies the acceptance criteria. Two non-blocking findings below — one internal-doc inaccuracy worth fixing before merge, one minor consistency suggestion.

Test evidence

Authoritative CI conclusions for the reviewed head c30c025e30484f08cf0002a265b7c4ff36ce4a85 (read from the GitHub API): lib + python tests: success, lint (shellcheck + actionlint + ruff): success, and all five shards (modules-rest, modules-pin, python-pool, modules-large, monolith): success. Test evidence is present and green.

Acceptance criteria

  • AC1 (event-append subcommand, own-clock timestamp, {event, recorded_at} shape, append-only under .prflow/logs/) — satisfied. cmd_event seeds record from _iso(_now()), appends via O_WRONLY|O_APPEND|O_CREAT to .prflow/logs/phase-events/phase-events.jsonl.
  • AC2 (always exits 0; failed write breadcrumbs) — satisfied. The whole mkdir/open/write/close sequence is inside one try, OSError is caught with a specific breadcrumb, and every path returns EXIT_OK.
  • AC3 (Phase 3 prose: simplify start/end, reviewer dispatch/return, shadow entry, vendored leading token) — satisfied by the implementation; see Finding 1 for a doc mismatch about where the reviewer events live.
  • AC4 (Phase 2 prose at each durability-checkpoint boundary) — satisfied; both phase2-durability-checkpoint.sh sites (phase-2-implement.md, phase-2-sweeps-quality.md) received a phase2-checkpoint event.
  • AC5 (no new agent-volunteered duration field; no telemetry field changes meaning) — satisfied.
  • AC6 (no new lib/capability-profiles.json entry) — satisfied; Bash(.prflow/vendor/prflow/scripts/verification-flight.py:*) already grants the subcommand.

Repo-specific gates

  • Prompt-surface edit routing gate (extension) — discharged. The diff touches trigger globs, and issue #1853's workpad carries a Writing-skills evidence: marker with all four slots explicitly recorded (skill-loaded=yes, guidance-applied=yes, pressure-scenario=no, micro-tests=no).
  • Verification-evidence advisory — silent; the workpad carries a Verification evidence: marker.
  • .gitignore/.prflow/logs/phase-events/ is correctly placed after the !/.prflow/logs/ negation, so the per-run event log stays ignored.
  • Shipped-prose cost — the added skill-prose blocks are minimal (one-sentence recipe + a vendored-leading-token fence each); the shadow-review.md grammar trims are net token reductions with no accuracy loss. No unearned wording.

Findings

1. Important (behavior-inert) — canonical internal doc cites the wrong file for the reviewer events.
docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md (new "Phase-boundary event instrumentation" section) states phase3-reviewers-dispatch / phase3-reviewers-return are emitted "around reviewer dispatch (skills/review/phases/phase-3-agents.md)". This is false against the reviewed head: those two events are actually added in skills/implement/phases/phase-3-fix-loop.md (around the Phase 3.3 review-and-fix invocation and its return), and skills/review/phases/phase-3-agents.md is not modified by this PR and carries no such call. A maintainer following the canonical overview to phase-3-agents.md finds nothing. The event names are correct; only the file citation and the "around reviewer dispatch" characterization are wrong. This is internal (not consumer-shipped) documentation and does not affect runtime behavior, hence non-blocking — but it should be corrected in this PR: change the parenthetical to skills/implement/phases/phase-3-fix-loop.md and reword to "around the Phase 3.3 review-and-fix dispatch/return".

2. Suggestion — JSON null payload is silently dropped without a breadcrumb.
In cmd_event (scripts/verification-flight.py), --payload null parses to None, which fails both the isinstance(payload, dict) arm and the elif payload is not None breadcrumb arm, so it records the base event with no stderr note — asymmetric with the non-object arm ([1,2]) which does breadcrumb. Not a swallowed defect (a null payload carries nothing to merge and the base event is still recorded), and the always-exit-0 contract holds. Consider letting the elif also breadcrumb the None case for symmetry, and adding a --payload null test to pin this currently-untested branch.

Verified correct (no action)

  • Reserved-key protection: event/recorded_at cannot be shadowed by a payload key (tested).
  • Non-blocking contract completeness: every line outside the try (_iso(_now()), json.dumps) is provably non-raising for the values in record.
  • Test coverage: TestPhaseEventAppend covers all seven behavioral arms of the ACs — record shape, accumulation, default location, payload merge + reserved-key protection, non-object/unparseable breadcrumbs, and the failed-write exit-0 path.

The01Geek added 2 commits August 25, 2026 15:44
…ayload

Addresses the two actionable findings from the review of c30c025.

Finding 1 — `docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md` cited
`skills/review/phases/phase-3-agents.md` for `phase3-reviewers-dispatch` /
`phase3-reviewers-return`. That file is untouched by this PR and carries no
such call; both events are emitted in
`skills/implement/phases/phase-3-fix-loop.md` around the Phase 3.3
`review-and-fix` dispatch and its return. The citation and the
"around reviewer dispatch" characterization are corrected.

Finding 2 — `cmd_event` reused `None` as the parse-error sentinel, so a
well-formed `--payload null` parsed to `None`, matched neither the dict arm
nor the `elif payload is not None` arm, and was dropped with no stderr
breadcrumb — contradicting the documented contract that a non-object payload
drops *with* a breadcrumb. A distinct `unparseable` sentinel now separates the
two, so `null` reaches the non-object arm while the parse-error path still
breadcrumbs exactly once. The overview's contract sentence is made exact about
both the `null` and the empty-payload cases.

Tests sweep the payload parser's non-object shapes together (`null`, number
and string scalars, array), the empty-string and empty-object shapes, and the
valid-falsy merge rows (`0` / `false` / `""`), and pin that the parse-error arm
does not double-breadcrumb.
@prflow-implementer

Copy link
Copy Markdown
Author

/prflow:review

@prflow-reviewer prflow-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE with notes (clean, well-tested change; only Suggestion-level advisories)

Issue Compliance

Reviewed against issue #1853: Phase 2 and Phase 3 carry no event instrumentation — criteria from the /prflow:implement workpad comment; scope unchanged. Requirement-based checklist items are included in the verification results below.

Criteria came from the /prflow:implement workpad comment — this run's authoritative set, possibly narrowed from the issue. No divergence between the workpad's criteria and the issue body's.

All six acceptance criteria are met:

  1. scripts/verification-flight.py gains an event <name> subcommand that appends a {"event", "recorded_at"} record — recorded_at produced by the helper's own clock via _iso(_now()), never the caller — to an append-only JSONL log at .prflow/logs/phase-events/phase-events.jsonl (O_WRONLY|O_APPEND|O_CREAT).
  2. ✅ The append always returns EXIT_OK (0); an OSError on the write path emits a specific stderr breadcrumb and the run continues.
  3. ✅ Phase 3 prose invokes the append with the vendored path as the command's leading token at /simplify start/end (phase-3-review.md), reviewer dispatch/return (phase-3-fix-loop.md), and shadow-review entry (shadow-review.md).
  4. ✅ Phase 2 prose invokes the append at both phase2-durability-checkpoint.sh boundaries (phase-2-implement.md, phase-2-sweeps-quality.md) — the only two such sites.
  5. ✅ No new agent-volunteered duration field is added; records are clock-authored and no existing telemetry field changes meaning.
  6. ✅ No new entry in lib/capability-profiles.json — the helper is already granted with a wildcard over its arguments.

Verification Checklist Results

Test evidence is the authoritative CI conclusion for the reviewed commit 9836f5d143ebe85f11c82c63cf4d3fd1935bf826 (per this run's engine ground-truth block): every required check is successlib + python tests, lint (shellcheck + actionlint + ruff), and all shards (monolith, modules-large, modules-rest, modules-pin, python-pool). The new TestPhaseEventAppend suite (9 test methods) covers the subcommand's contract directly, and the change was independently verified by four Phase-3 specialist reviewers (code-reviewer, silent-failure-hunter, pr-test-analyzer, comment-analyzer). Requirement compliance is established above (all six ACs met).

over-grade annotation: no finding flagged
intra-diff contradiction scan: no contradiction found
truthfulness sweep: no finding promoted

Code Review Findings

🟡 Suggestion / Minor

  1. skills/review-and-fix/references/shadow-review.md sits at ~61,747 bytes against the 61,750-byte reference-size ceiling — a ~3-byte margin. It passes today (the +125-byte event block is offset by ~68 bytes of prose trimming), but any future prose addition to this file will trip lint-reference-size.py; the remedy then is to trim, not exempt. (raised by 1/4 agents)
  2. scripts/verification-flight.py:304os.write(fd, line.encode("utf-8")) does not check its return against the buffer length, so a short write would append a truncated JSONL line with no breadcrumb. Negligible in practice (small records, regular file, O_APPEND) and well within the best-effort contract; noted for completeness. (raised by 1/4 agents)
  3. Three test-body comments restate the assertion on the line immediately below them, which §2.3's restatement rule discourages: lib/test/test_verification_flight.py:83, :128, :183. Behavior-inert test narration. (raised by 1/4 agents)
  4. Documentation/attestation drift (not in the shipped code): the PR description's "Changes" section and the issue #1853 workpad's Writing-skills evidence: marker both name skills/review/phases/phase-3-agents.md as carrying the reviewer dispatch/return events, but the diff actually places them in skills/implement/phases/phase-3-fix-loop.md. The shipped docs/internal/DEVFLOW_SYSTEM_OVERVIEW.md is correct; only the PR body prose and the writing-skills marker's file list are stale. No code or shipped-doc falsehood — the prompt-surface routing evidence gate is still discharged (all four dispositions recorded: skill-loaded=yes guidance-applied=yes pressure-scenario=no micro-tests=no). (raised by orchestrator)
  5. skills/implement/phases/phase-3-review.md — the phase3-simplify-end fence sits after the commit/push block but before the "if /simplify made no changes, skip the commit" note; it correctly fires regardless, but reads slightly out of order. Purely cosmetic. (raised by 1/4 agents)

Verdict Criteria

  • No verification-checklist FAIL or INCONCLUSIVE (test evidence: CI all-green on the reviewed commit).
  • No review-agent finding at or above the critical threshold; all findings are Suggestion-level.
  • No self-contradicting-diff finding (the shipped diff's docs/comments/tests are accurate against HEAD).
  • Only findings below the verdict threshold present → APPROVE with notes.

Prompt-surface routing evidence gate (DevFlow-repo policy)

The diff touches trigger globs (skills/implement/phases/*.md, skills/review-and-fix/references/*.md). The gate is discharged: issue #1853's workpad carries a Writing-skills evidence: marker with all four dispositions explicitly recorded — skill-loaded=yes, guidance-applied=yes, pressure-scenario=no, micro-tests=no. A no never draws a finding on its own.

Verification-evidence advisory (non-blocking)

The workpad records Verification evidence: for the run (§2.4: harness-python-guards module 45 passed 0 failed; full test_verification_flight.py 121 OK; ruff clean), and the workpad carries checkpoint rows (cloud tier). Advisory clause silent.

Shipped-prose cost audit (DevFlow-repo policy)

The added skills/** prose is minimal and earns its place: each insertion is one recipe sentence plus a vendored-leading-token fence carrying the required instruction (append the boundary event, best-effort). No restated rule, no derivable mechanics, no structural narration. No cost finding.

@The01Geek
The01Geek merged commit ee636d6 into main Aug 25, 2026
9 checks passed
@The01Geek
The01Geek deleted the issue-1853-phase-2-and-phase-3-carry-no-event branch August 25, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 2 and Phase 3 carry no event instrumentation, so a long implement run's interior cannot be explained

1 participant