Skip to content

aggregator(orchestration): section gate never checks tier/goal array elements — a [null] tier item or goal task passes validation and crashes buildAttention/todayFromSection #2417

Closed

Description

@dezren39

Finding

orchestrationSectionProblem / isOrchestrationSection (pm-stack/aggregator/src/feed.ts) check that the orchestration section's arrays exist, but never check the elements of tiers.needsDrew / tiers.needsOrchestrator, goals[].linedUp, or goals[].stalled. A section whose arrays contain null, a number, or an empty object passes the gate — the only gate on the live path (normalizeOrchestrationSection, feed.ts:200-201, used at sources/live.ts:661 and :911) — and then crashes the consumers that dereference the elements, instead of being diagnosed "invalid" and falling back to the derived view.

This is the element-level remainder of closed #949, which fixed the presence of the per-goal arrays (isGoalMeasurement, feed.ts:89-98, checks only Array.isArray on linedUp/stalled/gaps). Element shapes were never checked.

Evidence (main 5a34fa7, verified this run)

  • feed.ts:130-143 orchestrationSectionProblem: tiers.needsDrew/needsOrchestrator, dependencies, actions checked with Array.isArray only; goals checked with s.goals.every(isGoalMeasurement), and isGoalMeasurement (feed.ts:89-98) checks only that linedUp/stalled/gaps are arrays — never their elements, and never the tier arrays' elements at all.
  • derive.ts:299-305 tierItemSessionId(item) dereferences item.sessionId, then item.id.startsWith(...) — called on every tier element at derive.ts:412-419 (buildAttention) and again in the extras filter at :438.
  • derive.ts:674 g.linedUp.filter((t) => t.horizon === "today") and derive.ts:693 g.stalled.filter((s) => s.blocker) in todayFromSection dereference every goal element.
  • Laptop Bun probe (~/git/fleet/probe-2026-10-29-laneA-orch-elements/, real feed.ts/derive.ts from a fresh clone at 5a34fa7; control section passes and renders fine):
    • goals: [{...valid, linedUp: [null]}] → validator true, todayFromSection throws TypeError: null is not an object (evaluating 't.horizon')
    • goals: [{...valid, stalled: [null]}] → validator true, throws TypeError: null is not an object (evaluating 's.blocker')
    • tiers.needsDrew: [null] → validator true, buildAttention throws TypeError: null is not an object (evaluating 'item.sessionId')
    • tiers.needsDrew: [42] and [{}] → validator true, buildAttention throws TypeError: undefined is not an object (evaluating 'item.id.startsWith')

Why it matters

The orchestration section is produced by an LLM-run job writing JSON by hand — exactly the producer the gate exists to fail-soft against (#949's rationale). One malformed element in a tier list or a goal's lined-up/stalled list currently takes down the attention/today derivation for the whole feed instead of the section being diagnosed invalid (feed.ts's diagnoseOrchestrationSection machinery already exists to surface exactly this) and the aggregator falling back to its locally derived view.

Acceptance criteria

  • A section whose tiers.needsDrew/needsOrchestrator contains a non-object element (or one without a string id), or whose goals[].linedUp/goals[].stalled contains a non-object element, no longer crashes buildAttention or todayFromSection; the owning layer is pinned (gate rejects the section as invalid vs consumers skip bad elements) and applied consistently to both tier arrays and both goal arrays.
  • A poisoned section is treated as invalid/absent with the existing diagnosis wording instead of throwing on the live read path.
  • Regression tests cover at least: tier element null, tier element 42/{}, goal linedUp: [null], goal stalled: [null]; a well-formed section (existing orchestration fixtures/tests) still derives unchanged.
  • Triage records a ruling on the adjacent unchecked surfaces in the same gate: actions elements (sliced at derive.ts:709, rendered by field in render.ts) and dependencies elements — validate, tolerate, or explicitly leave with a reason.

Dedup: #949 (closed) covered missing per-goal arrays and missing provenance — presence, not element shapes; searches for "orchestration validator / tierItem null / isGoalMeasurement elements" returned only #949/#1110/#898, none covering element-level validation. The dns/aa/quota-current gate issues (#2412/#2415/#2416) are different files and fields.

Activity

  1. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    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/aggregatorPackage/app: aggregator (pm-stack in this repo)
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 29, 2026
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator-code-a (new-issue-creator job, lane A)
    • Session / ID: new-issue-creator-code-a run 2026-10-29 15:05 CDT
    • Found via: filed this run
    • Task: Triage the orchestration section gate's missing element-level validation (tier arrays + goal linedUp/stalled); pin gate-vs-consumer ownership and route a small_fix packet
    • Expectation: effort/xs triage — evidence and acceptance criteria are in the issue body; probe at laptop ~/git/fleet/probe-2026-10-29-laneA-orch-elements/ (main 5a34fa7)
    • Links: queue packet packet-2417-triage.json (repo-health-code-pilot)
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  5. 66 remaining items

  6. 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 2, 2026
  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — parked
    Agent: repo-worker-code (OpenChamber scheduled lane)
    Task: repo-health-code-pilot task 5314
    At: 2026-11-02T09:32:00Z
    State: parked
    Fresh evidence: #2417 remains OPEN and assigned to dezren39; canonical PR #4176 remains OPEN, non-draft, and Drew-authored against main on the task branch.
    Disposition: parked 30 minutes at the Drew-owned hard stop. No PR contents, diff, branch, worktree, CI, reviews, or source inspected/touched. Labels reflect parked state; work/assigned remains set.
    Resume condition: PR closes, ownership changes, or Drew explicitly hands off; revalidate first.
    Links: #2417 · #4176 · task 5314

  8. 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 2, 2026
  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up (ownership correction + CI disposition)

  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/aggregatorPackage/app: aggregator (pm-stack in this repo)app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions