Skip to content

fix(website): the unbound-tools group is stated, not implied by absence - #1026

Merged
gHashTag merged 1 commit into
mainfrom
site/unbound-group-always-stated
Sep 15, 2026
Merged

gHashTag merged 1 commit into
mainfrom
site/unbound-group-always-stated

Conversation

@gHashTag

Copy link
Copy Markdown
Owner

groups.triByAgent got its '-' bucket only when some tri command had no agent. gHashTag/t27#3833 gives every command an agent, so the key vanishes and qa/tools-spec-contract.mjs:194 reads undefined where it asks for a list — the shape the contract's own message promises, "unbound under '-'".

{
  "version": 1,
  "head_sha": "1e723e94c1b79634a4334174eac1803d9399d198",
  "summary": "The generator emitted the unbound-tools group only while some tri command had no agent, so a corpus where every command is bound loses the key entirely and the tools contract reads undefined where it asks for a list.",
  "changes": [
    "scripts/agents-from-specs.mjs: groups.triByAgent always carries the '-' key, empty when no tri command is unbound, instead of the key appearing only when the unbound set is non-empty.",
    "The contract at qa/tools-spec-contract.mjs:194 is unchanged: it already asserted the list, and the generator now produces the shape its own message describes."
  ],
  "tests": [
    {
      "command": "node scripts/agents-from-specs.mjs against a corpus where all 92 tools name an agent",
      "result": "the generator reported no problems and the tools catalog rebuilt",
      "status": "passed",
      "evidence": "tools 92 specs, with agents 92; agents 27 specs, typecheck ok 27/27, with tools 20, with skills 20"
    },
    {
      "command": "npm run check:tools",
      "result": "the tools contract passes against that same corpus",
      "status": "passed",
      "evidence": "92 cards, with agents 92, with skills 5; it failed with undefined !== [] before this change"
    },
    {
      "command": "npm run check:agents check:tools-site check:skills-catalog check:crons-catalog check:docs check:shared-core",
      "result": "the neighbouring catalog gates are unaffected",
      "status": "passed",
      "evidence": "all six pass with BOT_ROOT and T27_ROOT pointed at a nonexistent path"
    },
    {
      "command": "node --check and npx eslint on the changed file",
      "result": "the file parses and lints clean",
      "status": "passed",
      "evidence": "no output from either, which is what both emit on success"
    }
  ],
  "limitations": [
    "This is a shape fix in the generator; it changes no binding and no catalog content until gHashTag/t27#3833 lands and the nightly scan vendors it.",
    "The same file carried a sibling defect earlier today, an OR written as a sum, so this class of assumption may exist elsewhere in it and was not swept for.",
    "check:spec-catalog fails in a tree where vendored specs were edited by hand without re-running sync, core and atlas; that is an artefact of editing a vendored copy, not of this change."
  ],
  "tags": ["website", "tools", "agents", "generator"],
  "blog": {
    "title": "A group that existed only while it had members",
    "summary": "The tools catalog grouped tri commands by the agent that holds them, with the unbound ones under a dash, and the dash appeared only when something was unbound.",
    "outline": [
      "Grouping code that builds its keys from the data it sees will produce a key for every value present and none for a value absent, which is correct as a grouping and wrong as a contract when a reader expects the bucket to exist.",
      "The contract asked for the list of unbound commands and got undefined, because the corpus had just gained an agent for every command and the group therefore had nothing to create the key from.",
      "The fix is to seed the key rather than to relax the assertion: nothing is unbound is a statement the catalog should make, and an empty list makes it where a missing key only leaves it to be guessed.",
      "This is the second defect of the same shape in one file in a day, both of them structures that were true only while the data had a property nobody had written down as a requirement."
    ]
  }
}

groups.triByAgent got its '-' bucket only when some tri command had no agent,
so the day every command gained one the key vanished and qa/tools-spec-contract
read undefined where it asks for a list -- the same shape the contract's own
message promises, "unbound under '-'".

This is the second time today the same class of bug surfaced in this file: a
structure that held only while the data had a particular property, and broke
when the property went away. "Nothing is unbound" is a fact worth stating, so
the bucket is always there and empty when it is empty.

Группа '-' создавалась, только если существовали команды без агента. Как только
агент появился у всех, ключ исчез, и контракт получил undefined там, где просил
список. Теперь группа есть всегда и пуста, когда пуста.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gHashTag
gHashTag enabled auto-merge (squash) September 15, 2026 17:45
@gHashTag
gHashTag merged commit fa0a344 into main Sep 15, 2026
14 of 18 checks passed
github-actions Bot added a commit that referenced this pull request Sep 15, 2026
fix(website): the unbound-tools group is stated, not implied by absence (#1026)

groups.triByAgent got its '-' bucket only when some tri command had no agent,
so the day every command gained one the key vanished and qa/tools-spec-contract
read undefined where it asks for a list -- the same shape the contract's own
message promises, "unbound under '-'".

This is the second time today the same class of bug surfaced in this file: a
structure that held only while the data had a particular property, and broke
when the property went away. "Nothing is unbound" is a fact worth stating, so
the bucket is always there and empty when it is empty.

Группа '-' создавалась, только если существовали команды без агента. Как только
агент появился у всех, ключ исчез, и контракт получил undefined там, где просил
список. Теперь группа есть всегда и пуста, когда пуста.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant