Skip to content

UTS: shared spec clone for the skill creator and generated translator skills, plus inspector fixes - #565

Merged
sacOO7 merged 5 commits into
uts/translator-skills-docsfrom
uts/shared-spec-clone
Oct 15, 2026
Merged

sacOO7 merged 5 commits into
uts/translator-skills-docsfrom
uts/shared-spec-clone

Conversation

@sacOO7

@sacOO7 sacOO7 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #558. This PR targets that PR's branch (uts/translator-skills-docs). Review and merge it after 558.

Summary

The UTS skill creator, and the uts-to-<lang> skills it generates, now read the spec from one shared local clone of ably/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 GitHub main (as uts-to-csharp does with gh 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-csharp skill.

These are docs and skill-creator changes only. No UTS spec or SDK code changes.

Why

  • Current specs, one revision. A skill should translate against the latest spec, but every file it reads in one run must come from the same commit, and that commit must be recorded so re-sync and Upgrade/Fix can diff against it (guide §9.1). Fetching files from main one at a time meets the first need and breaks the second.
  • No surprises for local work. Developers keep their own clones on PR branches or with local edits. The skill must never move those.
  • One clone, any tool. The shared clone works the same from Claude Code and Codex, with no gh CLI or GitHub auth needed.

What changes

1. The shared spec clone

  • Location: ~/.ably/specification (%USERPROFILE%\.ably\specification on Windows), or wherever ABLY_SPEC_CLONE points. UTS_SPEC_CLONE still works as a deprecated alias, with a warning.

  • Which clone is used, in order:

    1. a --spec-clone path;
    2. the clone the skill is installed from;
    3. ABLY_SPEC_CLONE;
    4. ~/.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 (only origin/main moves), then git merge --ff-only --no-overwrite-ignore. Like a default git fetch, the fetch moves the remote-tracking origin/main even after a force-push on GitHub (the old value stays in its reflog); local branches are only ever fast-forwarded;
    • only when main is 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, and main is strictly behind;
    • every other state (another branch, detached, dirty, ahead, diverged, an operation in progress, a non-Ably origin) is reported and left alone;
    • it never pulls, resets, stashes, switches branches or overwrites ignored files.
  • 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 main predates the skill creator, the run stops with SPEC_CLONE_INCOMPLETE and 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_SYNC

ABLY_SPEC_SYNC (ask by default, auto or off) 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.

Clone in use What happens
Shared clone (ABLY_SPEC_CLONE, else ~/.ably/specification), however it was found: by default, a path into it, or a skill installed from it Depends on ABLY_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-Ably origin is still asked about: use as is, or stop).
• off: no network; the staleness warning says the sync is off (SYNC_OFF).
Another clone named with --spec-clone ABLY_SPEC_SYNC doesn'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."
A skill installed from a clone that isn't the shared one ABLY_SPEC_SYNC doesn't apply. Never synced or asked about; the same warning, worded "the clone this skill is installed from".
Generated skill given a module path into another clone ABLY_SPEC_SYNC doesn't apply. Never synced or asked about; fixed warning.
Generated skill given a bare module name, or a module path into the shared clone It's the shared clone, so the first row applies.
No clone yet The first clone is offered and always needs approval, whatever ABLY_SPEC_SYNC is.

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; under off, the full gap audit runs instead).

3. Generated uts-to-<lang> skills

The 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 new R-spec-sync check 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-csharp skill:

# Problem Fix
B1 The "fetches docs from main" check missed gh api …/contents/…, so a skill doing exactly that passed Detects gh api and api.github.com fetches, and flags any without a ref= as unpinned
B2 Harness self-tests were found by file name only, so Orient reported 0 when 4 existed Also searches from the harness root, reported as "candidates by location"
B3 The .claude/skills path check fired on a harness README's link to the skill Applied only to the skill's own files
B4 CI evidence was read only from .github/workflows Follows the scripts the workflows call, and build tools named without a file (make/gmake with -C/-f, rake, cake/dotnet cake) to their default entrypoints, and says which files it read
B5 When the recorded spec commit wasn't in the clone, Orient dropped the Changes line silently and still recommended a diff-driven upgrade Says so, and recommends the full gap audit
B6 No check that a usage guard exists (a guide MUST) New B-usage-guard check

Files

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:

Area Files
Scripts spec_clone_info.py (order, new fields), orient.py (no-clone and too-old outcomes, with the bootstrap offer deferred until after STOP-16; specSync and 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 creator docs 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 templates
Guide uts/docs/writing-uts-spec-translator-skills.md
README uts/README.md: the install section now installs from the shared clone

Review guide

  1. Start with references/orient.md §13.9, which describes the whole design in one place.
  2. Then scripts/ensure_spec_clone.py, especially locate() (which clone, and whether it's synced) and the update path (the safety checks before the fast-forward).
  3. Then the guide's §9.4 and the README install section, for the user-facing story.
  4. The SKILL.md changes are small, because the skill must stay within its compaction limits.

How this was validated

  • Clone script: 76 scenario checks pass. They use temporary repos with local bare origins and a temporary home folder, with no network, and log every git command. They cover:
    • each clone state: missing, behind, up to date, ahead, diverged, dirty (staged too), another branch, detached, merge or rebase in progress;
    • an ignored or untracked file in the fast-forward's way;
    • a held, stale or unremovable lock; offline; a read-only .git; a fork as origin;
    • each ABLY_SPEC_SYNC value; the env variables and the alias; symlinked and case-variant paths;
    • named clones never fetched or merged under any setting;
    • a main that predates the skill creator.
  • Orient: 23 scenario tests pass, covering eligibility still being decided when no clone exists, the sync offer per setting, warnings for named clones, and B5, including records that name no spec commit.
  • CI evidence (B4): 5 checks pass: make, make -C/-f, hyphenated targets, rake, dotnet cake, quoted commands, make in comments or step names not followed, and untracked entrypoints not followed.
  • Real repos (read-only): Orient on ably-dotnet's uts-to-csharp branch reports S3 and recommends the full gap audit, with B1–B6 reported correctly. ably-cocoa reports S3 with no regressions. git status and HEAD were unchanged in every repo.
  • Limits and hygiene:
    • 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-checker and git diff --check are clean;
    • links across all uts/**/*.md files: 0 broken.
  • Independent review: fresh Opus reviewers checked the full branch against the design, in several rounds, for completeness, safety, consistency across every file (the rule, the clone order, SHOULD vs MUST, result codes, cross-references) and agreement between scripts and docs. Every finding was fixed, and the last round found nothing missing. Copilot's three review comments were checked the same way: Orient's missing-commit branches are now explicit, the fetch's effect on origin/main is 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

  • Other ideas from the uts-to-csharp review: 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.
  • Upgrading ably-dotnet's uts-to-csharp skill. That is an SDK-repo change, to be run with the fixed inspector.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Comment thread uts/skills/uts-to-lang-skill-creator/scripts/orient.py Outdated
Comment thread uts/skills/uts-to-lang-skill-creator/scripts/ensure_spec_clone.py
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
sacOO7 force-pushed the uts/shared-spec-clone branch from 20d360c to f09a855 Compare October 14, 2026 18:21
@sacOO7
sacOO7 merged commit 68afd51 into uts/translator-skills-docs Oct 15, 2026
2 checks passed
@sacOO7
sacOO7 deleted the uts/shared-spec-clone branch October 15, 2026 05:52

This branch was successfully deployed

1 active deployment
staging/pull/565 — f09a8558 Deployed Oct 14, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants