fix(website): the unbound-tools group is stated, not implied by absence - #1026
Merged
Merged
Conversation
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
enabled auto-merge (squash)
September 15, 2026 17:45
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
groups.triByAgentgot its'-'bucket only when some tri command had no agent. gHashTag/t27#3833 gives every command an agent, so the key vanishes andqa/tools-spec-contract.mjs:194readsundefinedwhere 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." ] } }