Skip to content

task_lifecycle_gate observability and recursion-marker hardening + dispatch_gate inline-mission default-flip #670

@michael-wojcik

Description

@michael-wojcik

Background

Three smaller PR #663 follow-ups grouped by theme: dispatch-gate observability, dispatch-gate recursion-marker robustness, and post-calibration default-mode flip for the inline-mission gate.

Task A — F12 None-actor observability

task_lifecycle_gate.evaluate_lifecycle currently skips advisory rule emission when trustworthy_actor_name(...) returns None (cannot resolve the actor from the harness-trustworthy actor-identity chain). Skip-on-unresolvable was an explicit decision documented in test_skips_when_actor_unresolvable and architect-blind's S-2 sketch.

Add observability so this skip is not invisible:

  • Counter-incrementing log when the gate skips due to None actor (stderr only, not journal — keep PostToolUse silent)
  • Optional metrics endpoint or ~/.claude/teams/{team}/lifecycle-skip-count.json writeback for periodic audit
  • Counter-test: synthesize a None-actor input and confirm the counter increments

The goal is to detect drift if None-actor cases become common (would indicate the actor-identity chain is broken upstream). Surfaced as architect-blind sketch D.

Task B — F12 recursion-marker harness-stamp

task_lifecycle_gate emits metadata.completion_disputed writebacks when it detects a self-completion outside the carve-outs. To prevent the writeback itself from re-triggering the gate (recursion), it sets metadata.gate_writeback as a self-skip marker.

The current marker is a plain boolean. Harden against drift by stamping the harness identity into the marker:

metadata["gate_writeback"] = {
    "v": 1,
    "stamped_at": utc_iso(),
    "harness": pact_session_context.get("plugin_root", "unknown"),
}

Read-side: skip when gate_writeback["v"] == 1 AND the writeback is recent (within session). This bounds the marker's effect to the current harness; an old session's marker on a re-loaded task does not skip a new gate evaluation.

Surfaced as security-blind S-12 sketch E.

Task C — Post-calibration default-flip for inline-mission gate

PACT_DISPATCH_INLINE_MISSION_MODE ships in v4.1.3 with default warn (advisory) and tri-state values warn|deny|shadow. Default is warn so calibration data accumulates without blocking production dispatches.

After ~30 days of warn-mode telemetry:

  1. Review the long_inline_mission warn-rate. If <1% of dispatches trip the threshold (800 chars), confidence that the threshold is well-calibrated.
  2. Flip default to deny. Add migration note. Bump plugin minor version (this changes user-observable behavior).
  3. Existing warn users can opt out via env-var.

If the warn-rate is higher than expected (>5%), the threshold needs adjustment before flipping. Use the warn-mode data to find the right threshold. The 800-char number was a security-team estimate, not empirical.

Surfaced by backend-coder-blind in PR #663 review as F-11.

Relationship to PR #663

Test plan

Task A: parametrized tests for None-actor cases. Counter-test by removing the counter increment and confirming an audit test catches missing telemetry.

Task B: round-trip test that asserts a v1 marker round-trips through evaluate_lifecycle without re-triggering the gate. Counter-test by stripping v from the marker and confirming the gate re-evaluates.

Task C: the flip is a one-line default change plus migration note. Test plan is calibration-data review, not unit tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions