Skip to content

sync_research_workflow: parseable-but-wrong-shaped manifest/profile JSON crashes with a raw traceback (or a misleading 'missing directory: .agents/skills/r') instead of the tool's clean diagnostic #2800

Closed

Description

@dezren39

Summary

scripts/sync_research_workflow.py parses three JSON documents with json.loads and then uses the results as dicts with no shape validation: the target's upstream manifest (.agents/research-workflow.upstream.json), and the source/target repository profiles (.agents/research-workflow.json). A document that is syntactically valid JSON but the wrong shape — a top-level array, a files value that is a list or string, a numeric workflow_docs, a non-string lifecycle_skills element — raises TypeError/AttributeError, which main() does not catch (it catches only ValueError, OSError, KeyError, subprocess.CalledProcessError at :173). The tool therefore dies with a raw traceback naming Python internals instead of its own contract: a one-line diagnostic on stderr and exit 1.

One shape is actively misleading rather than crashing: a string lifecycle_skills is iterated character by character, so the tool reports missing source directory: .agents/skills/r — blaming a directory that was never configured.

Evidence (main c90fcbc7, script md5 319a3caa2fdbf117b4ba2a3867dfdf1d, identical in the sandbox clone and the laptop primary)

Laptop probe (~/git/fleet/probe-2026-10-30-laneB-syncshape/probe.py, real module, fixture repos built exactly like scripts/tests/test_sync_research_workflow.py):

Case Result
control: valid fixture, --check after a --write returns cleanly
manifest with a JSON syntax error JSONDecodeError (a ValueError) — caught by main, clean error + exit 1 ✅
manifest = [] (top-level array) TypeError: list indices must be integers or slices, not str — not caught
manifest = {"files": ["x"]} AttributeError: 'list' object has no attribute 'keys' (:112) — not caught
manifest = {"files": "abc"} AttributeError: 'str' object has no attribute 'keys' — not caught
source profile = [] AttributeError: 'list' object has no attribute 'get' (:46) — not caught
source profile workflow_docs: 123 TypeError: argument should be a str or an os.PathLike… not 'int' (:47) — not caught
source profile lifecycle_skills: [123] TypeError: unsupported operand type(s) for /: 'PosixPath' and 'int' — not caught
source profile lifecycle_skills: "research-workflow" (string) misleading ValueError: missing source directory: .agents/skills/r (iterates the string's characters)

Relevant code: profiles loaded at scripts/sync_research_workflow.py:44-47; manifest loaded at :96-99 and used as old["files"].get(...) / old["files"].keys() at :109, :112, :122; the catch tuple at :173 omits TypeError and AttributeError.

Why it matters

This script is the guard that keeps the mirrored research-workflow copy honest: it is supposed to refuse bad states with a readable reason ("local edits need reconciliation", "workflow mirror differs"). The manifest and profiles are exactly the files that get hand-edited, truncated by a bad merge, or regenerated by another tool — the moment the guard is most needed is the moment its input is malformed. Today that moment produces a traceback that points at PosixPath arithmetic instead of at the offending file and field, and the string-lifecycle_skills case sends the operator hunting for a directory named r. Sibling tool scripts/research_workflow.py already validates its profile shapes with isinstance checks (profile() at :77-102); the sync tool does not.

Acceptance criteria

  • A parseable-but-wrong-shaped manifest or profile (top level not an object; files not an object of string digests; workflow_docs not a string; lifecycle_skills not a list of strings) fails with a one-line diagnostic naming the file and the offending field, on stderr, exit 1 — no traceback.
  • A string lifecycle_skills is rejected as a shape error, never iterated as characters.
  • Valid fixtures behave exactly as before: the four existing tests in scripts/tests/test_sync_research_workflow.py still pass.
  • New tests cover at least: top-level-array manifest, non-object files, non-object profile, and string lifecycle_skills.
  • Scope is validation at the load sites only — no change to the sync/check semantics, the manifest format, or the local-edit protection rules.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    app/research-workflowscripts/research_workflow* + sync_research_workflow tooling
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 30, 2026
  2. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (new-issue-creator-code-b)
    • Session / ID: cron job new-issue-creator-code-b, run 2026-10-30 13:50 CDT
    • Found via: filed this run
    • Task: Triage the wrong-shaped JSON validation gap in scripts/sync_research_workflow.py (manifest + profiles) and route a small_fix packet
    • Expectation: effort xs — done when wrong-shaped manifest/profile input fails with a one-line diagnostic naming the file and field (no traceback, no character-iterated lifecycle_skills), with shape tests added and the existing sync tests green
    • Links: evidence probe on laptop ~/git/fleet/probe-2026-10-30-laneB-syncshape/ (real module, md5-matched to main c90fcbc)
  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 31, 2026
  4. 12 remaining items

  5. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 1, 2026
  6. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  7. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 1, 2026
  8. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  9. added a commit that references this issue on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/research-workflowscripts/research_workflow* + sync_research_workflow toolingeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions