Repository navigation
UTS: shared spec clone for the skill creator and generated translator skills, plus inspector fixes - #565
Merged
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved correctness and safety issues remain in the reviewed changes.
3 open findings
What changed in this PR
This PR adds shared spec-clone synchronization for UTS skills and improves inspector and Orient diagnostics.
Changes:
- Adds clone discovery, locking, syncing, and provenance reporting.
- Updates documentation, templates, and generated-skill guidance.
- Improves fetch, CI, harness, usage-guard, and sync checks.
Unresolved findings affect clone safety, missing-SHA handling, inspector checks, and installation.
| File | Reviewed changes |
|---|---|
uts/skills/uts-to-lang-skill-creator/SKILL.md |
Updates creator workflow and clone-sync rules. |
uts/skills/uts-to-lang-skill-creator/scripts/spec_names.py |
Uses shared clone discovery. |
uts/skills/uts-to-lang-skill-creator/scripts/spec_clone_info.py |
Reports clone state and provenance. |
uts/skills/uts-to-lang-skill-creator/scripts/orient.py |
Adds sync planning and reporting. |
uts/skills/uts-to-lang-skill-creator/scripts/inspect_existing_skill.py |
Improves inspector checks and sync validation. |
uts/skills/uts-to-lang-skill-creator/scripts/ensure_spec_clone.py |
Adds shared clone management and synchronization. |
uts/skills/uts-to-lang-skill-creator/references/upgrade-existing-skill.md |
Adds sync-aware upgrade guidance. |
uts/skills/uts-to-lang-skill-creator/references/upgrade-diff-driven.md |
Documents clone and provenance requirements. |
uts/skills/uts-to-lang-skill-creator/references/phase-6-validate-skill.md |
Adds synchronization validation guidance. |
uts/skills/uts-to-lang-skill-creator/references/phase-5-generate-skill.md |
Documents generated-skill sync behavior. |
uts/skills/uts-to-lang-skill-creator/references/phase-4-design-record.md |
Adds the synchronization design decision. |
uts/skills/uts-to-lang-skill-creator/references/orient.md |
Documents synchronization design and recovery. |
uts/skills/uts-to-lang-skill-creator/references/glossary.md |
Defines synchronization terminology. |
uts/skills/uts-to-lang-skill-creator/references/failure-modes.md |
Documents synchronization failures. |
uts/skills/uts-to-lang-skill-creator/assets/templates/skill-gap-audit.md |
Adds clone provenance fields. |
uts/skills/uts-to-lang-skill-creator/assets/templates/repo-profile.md |
Records clone state. |
uts/skills/uts-to-lang-skill-creator/assets/templates/final-report.md |
Records synchronization outcomes. |
uts/skills/uts-to-lang-skill-creator/assets/templates/design-record.md |
Adds clone and decision-record fields. |
uts/skills/uts-to-lang-skill-creator/assets/templates/acceptance-checklist.md |
Adds synchronization criteria. |
uts/README.md |
Updates shared-clone installation instructions. |
uts/docs/writing-uts-spec-translator-skills.md |
Defines generated translator-skill requirements. |
🧠 Review effort: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
New scripts/ensure_spec_clone.py is the only script that touches the network or changes the spec clone, and only on approval. It locates the clone (an explicit path, else the clone the skill is installed from, else ABLY_SPEC_CLONE, with UTS_SPEC_CLONE read as a deprecated alias, else ~/.ably/specification), reports its git state and staleness offline, and on request fetches origin main or fast-forwards main with `git merge --ff-only --no-overwrite-ignore`, only when main is checked out, clean, has no operation in progress and is strictly behind. Every other state is reported and left alone; it never pulls, resets, stashes, checks out or switches branches. Like a default git fetch, the fetch updates origin/main and changes no local branch. The sync runs only when the clone in use is the shared clone (ABLY_SPEC_CLONE, else ~/.ably/specification), however it was found, comparing directories with symlinks resolved and the filesystem's case rules. Any other clone is never fetched or changed, whatever ABLY_SPEC_SYNC (ask, auto, off) says: --fetch and --update report NOT_SYNCED_NAMED_CLONE, and syncEligible and notSyncedWarning carry a static warning to keep it up to date. A short lock in the clone's git directory serialises concurrent syncs; a lock that can't be written (a sandbox) is FETCH_FAILED with the clone used as it is, and a stale lock that can't be removed ends in LOCKED. The last fetch is the latest of the origin/main reflog, a FETCH_HEAD naming main from this clone's origin, and the clone itself. The user's core.sshCommand is kept, with BatchMode added. spec_clone_info.py and spec_names.py resolve the clone by the same precedence. A clone whose checkout lacks the guide or the skill (a revision that predates them) is SPEC_CLONE_INCOMPLETE, with a message saying how to proceed.
Inspector: - X-fetch-main also catches docs or specs fetched with `gh api` or the GitHub contents API, counting as unpinned only fetches that read the default branch. - Harness self-tests are also found by location: untagged test files under the harness root that use a type the harness declares, reported as candidates to confirm by reading. - The X-* checks read only the skill's own files (SKILL.md, references/, scripts/), not the harness README. - CI evidence follows the scripts the workflows call and build tools named without a file (make with -C/-f, rake, cake or dotnet cake), matched only in commands and as options at the start of a word, and says which files it read. - New B-usage-guard check: a usage line and a stop when the argument is missing. New R-spec-sync check flags a generated skill with a missing or stale copy of ensure_spec_clone.py. Orient stays offline: it runs the eligibility check even without a clone, shows the clone's kind, branch and staleness (a WARNING after 30 days without a fetch, noting when the sync is off), reports leftover partial clones, and reports in specSync what the sync should offer after STOP-16. A clone that isn't the shared clone gets the static not-synced warning first and no sync offer. When the recorded spec SHA isn't in the clone, the State summary says so instead of dropping the Changes line, and S2 recommends the full gap audit, since diff-driven can't diff.
New references/orient.md 13.9 holds the rules: which clone (an explicit path, the clone the skill is installed from, ABLY_SPEC_CLONE, ~/.ably/specification), the bootstrap offer, the one question per run through STOP-6 (or the user's ABLY_SPEC_SYNC, with how auto and off handle a missing recorded spec SHA), the fast-forward-only update rule, its outcomes, staleness, what is recorded, and concurrency. It states that only the shared clone is synced; any other clone gets a static warning and is the user's to keep current. 13.4 lists every Spec clone and Changes line variant and every exit-1 code. SKILL.md gains the sync as a Step 0 item between STOP-16 and STOP-1, an exception for it in 2.2, STOP-6 from Phase 0, and a shorter 2.6. New decision D-32 ships a copy of ensure_spec_clone.py in generated skills: Phase 5 copies it and writes the step 0 row, Phase 6 path 11 exercises the sync paths, and Upgrade/Fix pre-fills it. The glossary, failure modes, templates and Phase 4 reference follow.
Guide 9.4, retitled "One pinned local clone, brought up to date first", makes it a SHOULD that a uts-to-<lang> skill syncs the spec clone before reading anything: by default the shared clone the skill creator uses, fast-forward only and with consent (or the user's ABLY_SPEC_SYNC), and otherwise reported and asked. A module path into another clone is used as it is, with a warning, and never synced. 3.1 lists scripts/ensure_spec_clone.py (SHOULD); step 0, the frontmatter and argument rules, the resolver's specRepo, 3.3, 8.1, 9.1, 9.2, 13, the checklist and the patterns to avoid follow.
The README's install section now keeps one shared clone at ~/.ably/specification (or ABLY_SPEC_CLONE), clones it if absent, and symlinks the skill from it. It explains the per-run sync and ABLY_SPEC_SYNC, that only the shared clone is synced, how to test a branch with a worktree, and what happens when the shared clone's main doesn't contain the skill creator yet.
sacOO7
force-pushed
the
uts/shared-spec-clone
branch
from
October 14, 2026 18:21
20d360c to
f09a855
Compare
This branch was successfully deployed
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.


Summary
The UTS skill creator, and the
uts-to-<lang>skills it generates, now read the spec from one shared local clone ofably/specification, kept current safely. Before this, each skill found the spec its own way: any local clone the user pointed at, or files fetched from GitHubmain(asuts-to-csharpdoes withgh api). Clones could go stale without anyone noticing, and the fetches mixed revisions without recording which one was used.This PR also fixes six misreports in the creator's inspector and Orient that turned up when it was run on ably-dotnet's
uts-to-csharpskill.These are docs and skill-creator changes only. No UTS spec or SDK code changes.
Why
mainone at a time meets the first need and breaks the second.ghCLI or GitHub auth needed.What changes
1. The shared spec clone
Location:
~/.ably/specification(%USERPROFILE%\.ably\specificationon Windows), or whereverABLY_SPEC_CLONEpoints.UTS_SPEC_CLONEstill works as a deprecated alias, with a warning.Which clone is used, in order:
--spec-clonepath;ABLY_SPEC_CLONE;~/.ably/specification.Generated skills use the same order, without step 2.
The rule: the sync runs only when the clone in use is the shared clone, however it was found. A path that resolves to the shared clone's own folder counts as the shared clone (symlinks resolved, case-insensitive where the filesystem is).
How the shared clone is updated:
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main(onlyorigin/mainmoves), thengit merge --ff-only --no-overwrite-ignore. Like a defaultgit fetch, the fetch moves the remote-trackingorigin/maineven after a force-push on GitHub (the old value stays in its reflog); local branches are only ever fast-forwarded;mainis checked out, there are no tracked changes (staged or unstaged; git refuses rather than overwrite an untracked or ignored file), no merge, rebase or other git operation is in progress, andmainis strictly behind;origin) is reported and left alone;Approval: Orient stays offline. The sync is a separate Step 0 item, asked after the eligibility check (STOP-16), so an ineligible repo never gets a network question. It reuses the network stop point (STOP-6) and asks once per run. A standing preference can be set with
ABLY_SPEC_SYNC.The first clone is always asked for, even under
ABLY_SPEC_SYNC=auto.Too old for the skill: if the shared clone's
mainpredates the skill creator, the run stops withSPEC_CLONE_INCOMPLETEand says whether a sync would fix it.Offline or sandboxed: the run continues with the existing clone and reports how stale it is (a warning after 30 days without a fetch).
Records: the run records the spec commit used, the branch, which clone it was and how it was found, and the sync result. A short lock stops two runs syncing at once, and HEAD is re-checked before a commit is recorded.
2. Behaviour by clone and
ABLY_SPEC_SYNCABLY_SPEC_SYNC(askby default,autooroff) applies only to the shared clone. For any other clone it isn't read at all: there's no sync, no question and no network, whatever it's set to.ABLY_SPEC_CLONE, else~/.ably/specification), however it was found: by default, a path into it, or a skill installed from itABLY_SPEC_SYNC:•
ask: one question, then fetch and fast-forward when safe.•
auto: fetch and fast-forward when safe, without asking; otherwise used as is (a non-Ablyoriginis still asked about: use as is, or stop).•
off: no network; the staleness warning says the sync is off (SYNC_OFF).--spec-cloneABLY_SPEC_SYNCdoesn't apply. Never synced or asked about (NOT_SYNCED_NAMED_CLONE). Fixed warning: "Using the spec clone you named (<path>). It isn't synced; make sure its UTS specs are up to date."ABLY_SPEC_SYNCdoesn't apply. Never synced or asked about; the same warning, worded "the clone this skill is installed from".ABLY_SPEC_SYNCdoesn't apply. Never synced or asked about; fixed warning.ABLY_SPEC_SYNCis.A named clone that's missing a recorded spec commit is never fetched. The skill tells the user to fetch it, or offers the full gap audit. In the shared clone, a missing recorded commit is fetched by SHA as part of the sync (the same STOP-6 question; without asking under
auto; underoff, the full gap audit runs instead).3. Generated
uts-to-<lang>skillsThe guide now says a generated skill SHOULD use the same shared clone and update rule, by shipping a copy of
ensure_spec_clone.py. An explicit module path into any clone keeps working, and a bare module name MAY be accepted as the argument. The inspector has a newR-spec-synccheck that flags a generated skill whose copy of the script is missing or out of date; fetching the spec file by file is caught by the X-fetch-main check (B1).Guide sections updated: §3.1, §3.3, §4 step 0, §8.1, §9.1, §9.2, §9.4 (now "One pinned local clone, brought up to date first"), §13 and the appendix.
4. Inspector and Orient fixes
Found by running Orient on ably-dotnet's
uts-to-csharpskill:main" check missedgh api …/contents/…, so a skill doing exactly that passedgh apiandapi.github.comfetches, and flags any without aref=as unpinned.claude/skillspath check fired on a harness README's link to the skill.github/workflowsmake/gmakewith-C/-f,rake,cake/dotnet cake) to their default entrypoints, and says which files it readB-usage-guardcheckFiles
New:
uts/skills/uts-to-lang-skill-creator/scripts/ensure_spec_clone.py, the only script that uses the network or changes the clone. Standard-library Python 3.8+, and works when copied into a generated skill.Changed:
spec_clone_info.py(order, new fields),orient.py(no-clone and too-old outcomes, with the bootstrap offer deferred until after STOP-16;specSyncand sync status; the self-test candidates of B2; B5),inspect_existing_skill.py(B1–B4, B6,R-spec-sync),spec_names.py(same clone order)SKILL.md(frontmatter description and compatibility, Step 0 item, STOP-1/6/16 rows, §1.2–1.4, §2.2, §2.6, scripts table, D-32);references/orient.md(new §13.9 "Sync the spec clone", §13.1, §13.4, §13.6); glossary, failure modes, phase 4/5/6 references, both upgrade references, five templatesuts/docs/writing-uts-spec-translator-skills.mduts/README.md: the install section now installs from the shared cloneReview guide
references/orient.md§13.9, which describes the whole design in one place.scripts/ensure_spec_clone.py, especiallylocate()(which clone, and whether it's synced) and the update path (the safety checks before the fast-forward).SKILL.mdchanges are small, because the skill must stay within its compaction limits.How this was validated
.git; a fork asorigin;ABLY_SPEC_SYNCvalue; the env variables and the alias; symlinked and case-variant paths;mainthat predates the skill creator.make,make -C/-f, hyphenated targets,rake,dotnet cake, quoted commands,makein comments or step names not followed, and untracked entrypoints not followed.uts-to-csharpbranch reports S3 and recommends the full gap audit, with B1–B6 reported correctly. ably-cocoa reports S3 with no regressions.git statusand HEAD were unchanged in every repo.check_layout.py: the stop table ends at character 13,797 (limit 16,000) and the ground rules at 18,437 (limit 18,500); the description is 1,016 of 1,024 characters;editorconfig-checkerandgit diff --checkare clean;uts/**/*.mdfiles: 0 broken.origin/mainis documented, and CI evidence follows build-tool entrypoints.The scenario tests (76, 23 and 5) aren't committed, because the skill has no directory for script tests yet.
Not in this PR
uts-to-csharpreview: wait and assertion patterns, proxy-tier rules, CI evidence checks and harness self-tests. They're about how generated skills write tests, not how they handle the spec, and belong in a separate follow-up.uts-to-csharpskill. That is an SDK-repo change, to be run with the fixed inspector.