Skip to content

aggregator(project-config): registry validator accepts refs parseRef throws on and impossible openIssuesAsOf dates — a 'valid' config can break /program at render #3766

Closed

Description

@dezren39

Summary

validateProjectConfigs (pm-stack/aggregator/src/project-config.ts:659) is the registry's only guardrail — "strict in tests, never throws at render" — but two of its checks are looser than the code that consumes the values, so a registry entry can pass validation clean and still break or misrender a /program page:

  1. Refs the validator accepts, parseRef/refUrl throw on. The validator's REF_PATTERN (project-config.ts:641) is /^[^/\s]+\/[^#\s]+#\d+$/. The canonical parser (model.ts:452-456) is /^([\w.-]+\/[\w.-]+)#(\d+)$/ — exactly one slash, [\w.-] segments. Refs such as a/b/c#12, a$/b#12, and a+b/c#3 pass validation with zero errors, but parseRef throws not a repo-qualified ref and so does refUrl. Config tracked refs are rendered through refUrl (render.ts:5459, project detail page) and resolved through the shared ref set (allProjectRefs, project-config.ts:843), so one such entry turns a green registry test into a render-time throw.
  2. openIssuesAsOf is shape-only. The check at project-config.ts:696 is /^\d{4}-\d{2}-\d{2}$/, so 2026-02-30, 2026-13-01, 2026-00-00, and 2026-04-31 all pass. The value is rendered verbatim as the snapshot's date (render.ts:5462: "Open issues (GitHub search, <openIssuesAsOf>)"), so an impossible date ships as the stated provenance of the count. The same package already has the real-calendar helper for exactly this — isCalendarDate (derive.ts:75), used by rowDate/ymdLabel — the validator just doesn't use it.

Evidence (verified this run, main e764f4e)

Laptop probe ~/git/fleet/probe-2026-11-01-laneB-projcfg/probe.ts, importing the real validateProjectConfigs, parseRef, and refUrl from the laptop checkout (project-config.ts sha256 acd7cbe0…, model.ts sha256 f987be93…, both matching sandbox main e764f4e):

asOf 2026-02-30: errors=[]
asOf 2026-13-01: errors=[]
asOf 2026-00-00: errors=[]
asOf 2026-04-31: errors=[]
asOf control 2026-02-28: []
ref a/b/c#12: validationErrors=[] parseRef=THROW: not a repo-qualified ref: a/b/c#12 refUrl=THROW
ref a$/b#12: validationErrors=[] parseRef=THROW: not a repo-qualified ref: a$/b#12 refUrl=THROW
ref a+b/c#3: validationErrors=[] parseRef=THROW: not a repo-qualified ref: a+b/c#3 refUrl=THROW
ref developing-today/code#1: validationErrors=[] parseRef=ok refUrl=https://github.com/developing-today/code/issues/1

Why it matters

The registry is edited by hand as the documented way to add a project ("Adding a project is a config change here"). The validator plus its test (validateProjectConfigs(PROJECT_CONFIGS) must equal [], test/program.test.ts:171) are the whole safety net for those edits. A check that disagrees with the consumer gives false confidence in both directions the fleet has already been bitten by: #2485 is the same failure shape one layer down (bad refs pass a gate, refUrl throws at render), filed for feed data; this is the static-registry instance of it.

Acceptance criteria

  • validateProjectConfigs rejects any ref that parseRef (model.ts) would reject — ideally by sharing one pattern/parser instead of a second hand-rolled regex — including extra path segments and characters outside [\w.-] in either segment.
  • openIssuesAsOf validation requires a real calendar date (month 01-12, day valid for the month/year, leap years correct), reusing isCalendarDate from derive.ts or an equivalent check; 2026-02-30, 2026-13-01, 2026-00-00, 2026-04-31 produce errors, 2026-02-28 and 2024-02-29 do not.
  • Regression tests in test/program.test.ts alongside the existing "validation catches duplicates and malformed entries" cases; the shipped PROJECT_CONFIGS still validate to [].
  • No render-path changes needed: this is the validator agreeing with its consumers.

Related: #2485 (feed taskRefs, same throw-at-render shape, different gate and data path).

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    on Nov 1, 2026
  2. developing-today commented on Nov 1, 2026

    @developing-today
    Contributor
  3. added
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    effort/sSmall: expected under 2 hours of work
    app/aggregatorPackage/app: aggregator (pm-stack in this repo)
    on Nov 1, 2026
  4. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (lane B), new-issue-creator-code-b
    • Session / ID: new-issue-creator-code-b run, Sun 2026-11-01 09:50 CST
    • Found via: filed this run
    • Task: Triage the project-config registry validator divergence (REF_PATTERN vs parseRef, shape-only openIssuesAsOf) in pm-stack/aggregator/src/project-config.ts
    • Expectation: effort/s — make validateProjectConfigs reject any ref parseRef rejects and require a real calendar date for openIssuesAsOf (reuse isCalendarDate from derive.ts); regression tests in test/program.test.ts; shipped PROJECT_CONFIGS still validate to []
    • Links: related aggregator(registry): malformed taskRefs elements pass every gate and crash /sessions at render — an array of bad refs merges, persists, then refUrl throws #2485 (feed taskRefs, same refUrl-throws-at-render shape, different gate); evidence/probe: laptop ~/git/fleet/probe-2026-11-01-laneB-projcfg/ (real validateProjectConfigs/parseRef/refUrl @e764f4e7, project-config.ts sha256 acd7cbe0, model.ts sha256 f987be93)
  5. added
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  6. 18 remaining items

  7. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  8. added a commit that references this issue on Nov 3, 2026
  9. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: triaged (coverage pass)
    Coverage pass 2026-11-03: reviewed in the fleet-wide per-issue pass (drift / overlap / already-handled / stale / blockers / ordering). Verdict: still live; queued in repo-health-code-pilot as triage at priority 30 (priority/low). If this is already handled at current main, the triage closes it with evidence; if it needs splitting, the triage files the split.
    Found via: coverage pass 2026-11-03

  10. dezren39 commented on Nov 4, 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/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientqueue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobstatus/needs-triageNot yet triaged; needs a first looktask/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