Skip to content

#601 stage 5 (fix-forward): join_windows static count check misses output: sources #666

Description

@dkackman

Fix-forward for #601, filed by the lead agent (claude-opus-5-5 via anthropic) from the tester's failed final check of the parent. It concerns stage 3 (#630): join_windows' static window-count check. Plan v3 says the check fires when source names a knowable file, "asset:/output:, or a literal path the run may read". The output: arm doesn't fire. This is a build miss, not a plan gap.

Acceptance

C-F233 step 2, which already exists, so this stage needs no new spec. A wrong window count with an output: source must be refused at validate_workflow, at the join step's path, naming the needed count.

The failure, from the bounce (lem, develop @ b8aef18)

  1. The tester made a 124-frame source with run_workflow {"id":"cc","steps":[{"name":"cat","task":{"command":"concat_videos","arguments":{"videos":["asset:qa-cast/ep6-cold-open.mp4"]}},"result":{"content_type":"video/mp4","fps":24}}]}. Job 1b73865a4a47 wrote cc/20261007-030343-8f1db730/cc-cat.0-0.0.mp4.
  2. They ran validate_workflow on C-F229's RT (num_frames 33, overlap 8, cosine) with source_video: "output:cc/20261007-030343-8f1db730/cc-cat.0-0.0.mp4" and 4 windows (w0–w3).
    • Got: valid: true, no errors, no warnings, plan.list_entries.windows: 4.
    • Expected: an error at steps[1] naming 5 windows, as the same RT gives with asset:qa-cast/ep6-cold-open.mp4.
  3. The reference does resolve at validate: output:…/nope.mp4 is refused at variables.source_video with "Output '…/nope.mp4' not found under …/outputs".
  4. run_workflow on the step 2 workflow (job 17b72f533155) ran all 4 windows on the GPU and then failed at join: "join_windows needs 5 windows for a 124-frame source with num_frames 33 and overlap 8 (stride 25), got 4 - add 1 entry (index 4)". The run-time rule works. The static check misses the output: source, which costs a full GPU run that validate should have stopped.

Where to look (lead's reading, not verified)

  • dw/window_count_errors.py:96 calls resolve_probe_path(arguments.get("source"), base_dir, …).
  • dw/probe_paths.py:41-51 resolves output: through fetch_output(value), with no base_dir or workspace, and any exception there returns None, so the check stays silent.
  • Hypotheses: fetch_output resolves against a different outputs root at validate than the reference check does (the session workspace's), or it raises, or it returns a non-str. Then _frame_count gets nothing.
  • dissolve_frame_errors shares resolve_probe_path. If the cause is there, its output: arm is likely silent too. Fix it at the shared resolver and test both.

Tests

  • A unit test for window_count_errors with an output: source under a non-default workspace's outputs: short, exact and long lists.
  • The same for dissolve_frame_errors if the fix is in resolve_probe_path.

Deploy: server restart. Estimate: ~$2.

Activity

  1. added
    featureWork bigger than a fix: designed with Don, built in stages (harnest R11)
    owner:leadFeature lead's turn (harnest R11)
    stageA child issue: one stage of a feature
    on Oct 7, 2026
  2. added and removed
    owner:leadFeature lead's turn (harnest R11)
    on Oct 7, 2026
  3. dkackman commented on Oct 7, 2026

    @dkackman
    OwnerAuthor

    Built by the lead agent, model claude-opus-5-5 via anthropic.

    Shipped: 53225a9e (merge of feat/601-E-output-source-probe, fix commit on that branch). Deploy line: [deploy 08:10:12] deployed develop @ 53225a9e, healthy.

    Cause: none of the issue's hypotheses about fetch_output itself. dw/server/admission.py admit() makes the request workspace's assets the active library for every validate-time check (activate_asset_dir), but it left the output root at its default, which is the default workspace's outputs. The reference check passes root=workspace.outputs explicitly, so output: resolved there, but resolve_probe_path → fetch_output used the default root, missed, and the probe returned None, so the check stayed silent. The tester's cc/… output lives in their session workspace.

    Fix: admit() now also activates workspace.outputs as the output root (activate_output_root) for the same scope, which covers validate, submit and rerun. Because the fix is at the shared scope, it covers every probe that reads an output: source: window_count_errors, dissolve_frame_errors, slice preflight and the warnings.

    Plan items covered: plan v3, stage 3's static window-count check, "fires when source names a knowable file (asset:/output:…)". The output: arm now fires. Acceptance is C-F233 step 2, as the stage says.

    Tests (tests/test_admission.py, through POST /api/validate in a named non-default workspace whose outputs alone hold the source):

    • join_windows with an output: source and a 50-frame clip, 17/4: 3 windows refused at steps[1] ("needs 4 windows"), 4 clean, 5 refused.
    • dissolve_videos over output: members shorter than dissolve_frames is refused.
    • All three refusal cases fail without the fix and pass with it. Full pytest: the same 17 failures as develop before the change (Kandinsky-6 template catalog checks, local env), none new. ruff is clean.

    Deviations: the fix sits in admission's scope, not in resolve_probe_path as the issue guessed. The resolver was correct; it was being called with the wrong ambient root. No MCP shape changed.

  4. dkackman commented on Oct 7, 2026

    @dkackman
    OwnerAuthor

    Architecture review queued by the loop driver: this hand-off changed dw/ or dw_mcp/ on develop (447f0a6e5f..53225a9e3f: dw/server/admission.py). The reviewer checks it against docs/ARCHITECTURE.md before the tester verifies.

  5. dkackman commented on Oct 7, 2026

    @dkackman
    OwnerAuthor

    Architecture review: pass. Reviewer agent, model claude-opus-5-5 via anthropic.

    What I read: bdb4ae53 (the only #666 commit in 447f0a6e..53225a9e; under dw//dw_mcp/ it touches only dw/server/admission.py), against docs/ARCHITECTURE.md and item 3 of stage-c-guardrails.md.

    • Second owner: none. The fix doesn't re-derive the output root. It calls the owner's own dw/runs.py activate_output_root / deactivate_output_root, the same ContextVar pair that dw/workflow.py and dw/workflow_run.py already use. output: resolution and confinement stay in dw/runs.py / dw/security.py (map row "Reference confinement").
    • Build vs. buy: nothing hand-rolled.
    • The map: no new owner and no rule moved. The "Admission" row (dw/server/admission.py: admit, which checks a request once) still describes it, so no row update is needed.
    • Context: the reasoning is in the code comment in admit(), next to the existing asset-dir rationale. No CLAUDE.md changed.

    Nothing was waived (the issue has no arch-approved label). It goes to the tester for C-F233 step 2.

  6. dkackman commented on Oct 7, 2026

    @dkackman
    OwnerAuthor

    Verified. Tester agent, model claude-opus-5-5 via anthropic, against lem on develop @ 7f524c4.

    Acceptance is C-F233 step 2. No suite case carries pending: #666, so there were no pending lines to remove.

    Default workspace (session workspace):

    • run_workflow {"id":"cc", concat_videos(["asset:qa-cast/ep6-cold-open.mp4"]) → video/mp4 @24} ran as job 06523207f2e4. It wrote cc/20261007-082336-1221c529/cc-cat.0-0.0.mp4 (124 frames).
    • validate_workflow on C-F229's RT (33/8, cosine) with source_video: "output:cc/20261007-082336-1221c529/cc-cat.0-0.0.mp4":
      • 4 windows: valid: false, error at steps[1]: "join_windows needs 5 windows for a 124-frame source with num_frames 33 and overlap 8 (stride 25), got 4 - add 1 entry (index 4)".
      • 5 windows (exact): valid: true, no errors, list_entries.windows: 5.
      • 6 windows (w5 repeating index 4): valid: false at steps[1]: "… got 6 - drop 1 entry (index 5)".

    Non-default workspace (qa-ep115, the case the fix targeted):

    • The same concat ran with workspace: "qa-ep115" as job e5d8a4888f19 and wrote cc/20261007-082404-43c1a703/cc-cat.0-0.0.mp4.
    • validate_workflow(workspace="qa-ep115") with that output: source:
      • 4 windows: refused at steps[1], naming 5 (same message as above).
      • 5 windows: valid: true, plan.workspace: qa-ep115, output_dir …/qa-ep115/outputs.

    Nothing was queued by any validate. Both concat runs were removed with delete_output(job_id=…).

    I did not exercise the dissolve_videos output: arm the hand-off also mentions. The stage's acceptance is the join_windows check alone.

    Side note for the suite, not this fix: C-F229's RT as written has no top-level id, and validate_workflow refuses it with "'id' is a required property". I added "id": "rt" to run it. I'm filing that as a suite amendment on the harness repo.

  7. added
    status:verifiedTester confirmed the fix via a real MCP call
    and removed on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureWork bigger than a fix: designed with Don, built in stages (harnest R11)owner:testerTester's turn to actstageA child issue: one stage of a featurestatus:verifiedTester confirmed the fix via a real MCP call

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions