Skip to content

feat: add KB child topics resource - #285

Open
Ruari-Phipps wants to merge 6 commits into
mainfrom
feat/kb-child-topics
Open

feat: add KB child topics resource#285
Ruari-Phipps wants to merge 6 commits into
mainfrom
feat/kb-child-topics

Conversation

@Ruari-Phipps

@Ruari-Phipps Ruari-Phipps commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a ChildTopic resource for variant-scoped knowledge base topics, stored at child_topics/<variant_name>/<topic_name>.yaml. It subclasses Topic to reuse serialisation and reference validation, overriding only the parts that genuinely differ. Also fixes a pre-existing crash in flow steps that was uncovered while building it.

Motivation

Child topics (the platform's childOverwrites.knowledgeBase collection) had no representation in the ADK, so variant-scoped topics could not be pulled, diffed or pushed from a local project.

Despite the platform naming, these are not overrides of a base topic. Confirmed against the platform backend: a child topic is assigned its own unique ID independent of any base topic, and there is no ID-based link field between the two. They are modelled here as what they are: independent, variant-scoped topics.

Changes

Child topics

  • Add ChildTopic in src/poly/resources/child_topic.py, registered as child_topics, subclassing Topic
  • Store them under their own child_topics/<variant>/ tree, kept separate from the base topics in topics/ so discovery of the two can never overlap
  • Parse from the childOverwrites.knowledgeBase projection, resolving variant names from variantManagement
  • Infer the owning variant from the enclosing folder rather than storing it in YAML, mirroring how FlowStep resolves its parent flow
  • Route commands via command_type = "child_topic" so create/update/delete act on the child overwrites collection instead of the base topics
  • Validate that the variant exists, and that a name is unique across all topics and child topics in every variant

The uniqueness rule matches the server rather than being stricter or looser than it: getAllActiveTopics concatenates knowledgeBase.topics with childOverwrites.knowledgeBase.topics across all variants, and CreateChildTopicHandler inherits that check from CreateTopicHandler. Enforcing it locally turns what would be a push-time rejection into a message naming the clashing file.

Flow steps — pre-existing crash

FlowStep and FunctionStep resolve their flow via get_flow_id_from_flow_name, which returns nothing when a step's flow config is missing, renamed or unparseable. file_path then dereferenced the unset flow name, so any orphaned step crashed poly diff/status with AttributeError: 'NoneType' object has no attribute 'lower'.

Both now fall back to the folder as read from disk, so the path stays usable and the real problem is reported by validation instead of a stack trace. This bug predates the branch; ChildTopic initially inherited it by copying the FlowStep pattern.

Test fixtures

  • Read the fixture JSON as UTF-8. It was decoded with the platform default encoding, which corrupted non-ASCII content on Windows and made the resource look modified on every run.

Docs

  • Correct the resource-scaffolder agent guide, which documented a registration flow (_read_<type>_from_projection on SyncClientHandler, hand-editing RESOURCE_NAME_TO_CLASS) that no longer exists — registration is now the @register_resource decorator plus a from_projection classmethod on the resource

Test strategy

  • Added/updated unit tests
  • Manual CLI testing (poly <command>)
  • Tested against a live Agent Studio project
  • N/A (docs, config, or trivial change)

Checklist

  • ruff check . and ruff format --check . pass
  • pytest passes
  • No breaking changes to the poly CLI interface (or migration path documented)
  • Commit messages follow conventional commits

Ruari-Phipps and others added 2 commits August 25, 2026 16:40
Adds a ChildTopic resource for variant-scoped knowledge base topics, stored
at topics/<variant_name>/<topic_name>.yaml alongside the flat base topics.

ChildTopic subclasses Topic to reuse YAML serialisation, pretty-printing and
reference validation, overriding only what differs: the projection source
(childOverwrites.knowledgeBase), the variant-nested file path, discovery, and
the command type. Commands route through create/update/delete_child_topic so
they act on the child overwrites collection rather than the base topics.

The variant is inferred from the enclosing folder rather than stored in YAML,
mirroring how FlowStep resolves its parent flow. Validation checks that the
variant exists and that names are unique within the base topics plus that
variant's child topics; the same name may be reused across variants.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The guide described a registration flow that no longer exists: adding a
_read_<type>_from_projection method to a SyncClientHandler in platform_api.py
and hand-editing RESOURCE_NAME_TO_CLASS in project.py. Registration is now
driven by the @register_resource decorator, and each resource class owns its
own from_projection classmethod.

Also documents the parent-scoped resource pattern, the find_new_kept_deleted
name-recovery branch that cleaned-filename resources must opt into, and drops
the venv activation step that conflicts with uv run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Ruari-Phipps
Ruari-Phipps requested a review from a team August 25, 2026 15:40
@Ruari-Phipps
Ruari-Phipps requested a review from a team as a code owner August 25, 2026 15:40
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Ruari-Phipps and others added 2 commits August 25, 2026 17:05
The variant folder was written with clean_name(lowercase=False), producing
topics/Variant_1/topic_1.yaml while every other name in a topic path is
lowercased. A variant declared as "Variant 1" therefore never matched its
folder on disk, leaving variant_name unset, and file_path then crashed on
None while diffing.

Lowercase the variant folder and normalise both sides of the comparison, so
"Variant 1" resolves to topics/variant_1/. When a variant still cannot be
resolved, keep the folder as read from disk so file_path remains usable
during discovery; validate() continues to reject it, since variant_id is
left unset. file_path now raises a readable error rather than a TypeError
when there is no variant at all.

Also drop the non-ASCII fixture content and read the fixture JSON as UTF-8.
The fixture was the only non-ASCII file in the repo and was decoded with the
platform default encoding on Windows, corrupting the accented characters and
making the resource look modified on every run.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…teps

Child topic names were checked only against the base topics and the child
topics in the same variant. The platform requires a name to be unique across
the base topics and every child topic in every variant, so a cross-variant
duplicate passed locally and was rejected on push. Check every topic and
child topic regardless of variant, and name the clashing variant in the
error.

FlowStep and FunctionStep had the same latent crash that was just fixed for
child topics: get_flow_id_from_flow_name returns no flow when a step's flow
config is missing or unreadable, and file_path then dereferenced the unset
flow name, raising AttributeError while diffing. Fall back to the folder as
read from disk so the path stays usable, and raise a readable error when
there is no flow at all.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Base (main) PR Change
75.6% 75.8% +0.2% ✅

Changed file coverage

File Coverage Change
poly/resources/flows.py 88.9% +0.0% ✅
poly/resources/resource.py 89.8% +0.3% ✅
poly/resources/topic.py 100.0% +3.8% ✅

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