Dwie rzeczy, które nazywają się prawie tak samo
Jest 15 marca i w każdej rozmowie o Copilocie mam ten sam kłopot: pada słowo “workspace” i nie wiadomo, o którą z dwóch rzeczy chodzi.
Pierwsza to Copilot Workspace. Pokazano go 8 listopada 2023 na Universe jako most od zgłoszenia przez plan do pull requesta: bierzesz issue, dostajesz propozycję planu na kilka plików, akceptujesz, powstaje gałąź. GitHub zapisał wtedy “when Copilot Workspace is delivered in 2024” i to zdanie jest dziś całym moim stanem wiedzy. Nie mam przycisku, nie mam linku, nie mam nawet formularza, w którym mógłbym zostawić adres i powiedzieć, że stoję w kolejce. Pokaz z keynote’u to nie narzędzie do włączenia, a opisywanie interfejsu z cudzych slajdów nie jest pisaniem o warsztacie.
Druga to @workspace. Mała litera, małpa z przodu, agent w okienku Copilot Chat, wymieniony w changelogu GitHuba z 30 listopada 2023 obok @vscode i opisany dwa tygodnie wcześniej na blogu VS Code. Czat, w którym ten agent siedzi, jest od 29 grudnia ogólnie dostępny w stabilnym edytorze, czyli bez Insidersa i bez rozszerzenia Nightly, przez które prowadziłem czytelników w maju zeszłego roku. To jest ta rzecz, którą faktycznie mam.
Moja teza na marzec 2024 jest wąska i będę jej bronił przez cały wpis. Kontekst całego repozytorium w czacie to realna zmiana i realnie z niej korzystam, bo pytania o zestaw testów prawie nigdy nie mieszczą się w jednym pliku. Ta zmiana dotyczy jednak tego, czego model może się domyślić, a nie tego, kto podpisuje się pod diffem. Dostaję listę plików i propozycję zmian, recenzuję ją dokładnie tak, jak od kwietnia zeszłego roku recenzuję pojedynczą podpowiedź w linii, i dopiero potem coś z tego wkleję do zestawu. Most od issue do merge’a nie powstał od tego, że pytanie wolno zadać szerzej.
Stan warsztatu na dziś, żeby za rok było wiadomo, o czym mówię. Runner to Playwright 1.42.1 z 2 marca. Czat mam w stabilnym VS Code, na tej samej subskrypcji, którą płacę za szary tekst przed kursorem od czerwca 2022. 27 lutego Copilot Enterprise wszedł w ogólną dostępność i przyniósł trzy rzeczy: czat na github.com, bazy wiedzy i podsumowania pull requestów. W rozmowach te trzy rzeczy zlewają się w jedno zdanie o tym, że “GitHub ma już tego agenta z Universe”, więc powiem wprost: żadna z nich nie jest Copilot Workspace. Edytor Cursor nadal nie jest moim codziennym narzędziem.
Czego tu nie będzie. Nie będzie zdania, że dziś otwieram Workspace i prowadzę zgłoszenie do pull requesta, bo nie otwieram. Nie będzie tutoriala narzędzia, którego nie mam w rękach. Nie będzie przerzucania codziennej pracy do innej karty w przeglądarce tylko dlatego, że w marcu wyszedł kolejny model. Będzie jedno syntetyczne demo z mojego repozytorium z przykładami do bloga: page object, spec i notatka o logowaniu, jedno pytanie do @workspace i dwa skreślenia.
Co znaczy wieloplikowo w marcu
Szary tekst pracuje na tym, co ma nad kursorem. Tak było w technical preview dwa lata temu i tak jest dzisiaj: wejściem jest plik, wyjściem jest kolejna linia. Przy boilerplacie testów sprawdziłem, że sąsiednie otwarte zakładki też wchodzą do kontekstu, i to była cała moja wieloplikowość: otwierałem obok plik komponentu, żeby podpowiedź przestała wymyślać API, którego w projekcie nie ma. Działało, tylko dźwignią byłem ja i moje karty w edytorze.
@workspace odwraca tę kolejność. Nie zgaduję, co otworzyć, żeby model to zobaczył. Zadaję pytanie, a czat sam przeszukuje repozytorium, wybiera fragmenty, które uznał za pasujące, i dokleja je do promptu. Nad odpowiedzią pojawia się rozwinięcie z listą użytych referencji i to jest w całej tej funkcji najbardziej użyteczny element interfejsu: widzę, na czym model się oparł, zanim przeczytam, co z tego wywnioskował. Kiedy na liście pojawia się plik, którego się nie spodziewałem, jest to zwykle ważniejsza informacja niż sama odpowiedź.
Demo trzymam w repozytorium z przykładami do bloga, nie w projekcie, za który ktokolwiek mi płaci. Trzy pliki i jedna reguła biznesowa:
pages/LoginPage.ts- page object z lokatorami w klasie strony, w konwencji, którą ustawiłem przy refaktorze POM,tests/auth/login.spec.ts- jeden scenariusz poprawnego hasła,docs/auth-rules.md- notatka, że złe hasło zostawia użytkownika na formularzu i pokazuje komunikat “Nieprawidłowy e-mail lub hasło”.
import { Locator, Page } from '@playwright/test'
export class LoginPage {
readonly email: Locator
readonly password: Locator
readonly submit: Locator
constructor(private readonly page: Page) {
this.email = page.getByLabel('E-mail')
this.password = page.getByLabel('Hasło')
this.submit = page.getByRole('button', { name: 'Zaloguj' })
}
async open() {
await this.page.goto('/login')
}
async signIn(email: string, password: string) {
await this.email.fill(email)
await this.password.fill(password)
await this.submit.click()
}
}Pierwsze pytanie zadaję rozpoznawczo, przez /explain, i brzmi ono tak:
@workspace /explain Co pokrywa dziś zestaw testów logowania
i czego w nim brakuje względem docs/auth-rules.md?Odpowiedź jest poprawna w trzech punktach. Zestaw ma jeden scenariusz i jest to happy path. Notatka opisuje dwa zachowania, z których tylko jedno ma test. Page object nie ma metody ani lokatora, którym dałoby się przeczytać komunikat błędu. To jest ten rodzaj pytania, przy którym czat wypada najlepiej, i z tego samego powodu, który zapisałem w maju 2023: odpowiedź opisuje pliki, które model ma przed sobą, więc nie ma z czego zmyślać. Różnica względem maja jest zasięgiem kontekstu i statusem produktu, nie magią. Wtedy musiałem otworzyć plik w sąsiedniej karcie i wskazać go palcem, dziś wystarczy o niego zapytać.
Warto nazwać, czego ta funkcja w marcu 2024 nie robi, bo w rozmowach dostaje to w pakiecie. Nie uruchamia mojego testu i nie widzi, co runner wypisał w nocy. Nie wie, które selektory faktycznie istnieją w DOM uruchomionej aplikacji, bo indeksuje repozytorium, a nie działającą stronę. I nie ma jednego przycisku, który rozłożyłby odpowiedź na trzy pliki: pod blokiem kodu mam kopiowanie i wstawienie w miejscu kursora, czyli operacje na jednym pliku naraz, wykonywane moją ręką. Ten ostatni brak jest w tym miesiącu moim sprzymierzeńcem i cała następna sekcja jest o tym, dlaczego.
Plan, nie apply na ślepo
Drugie pytanie formułuję inaczej. Proszę o dwie rzeczy i wyraźnie o nic więcej: o listę plików, które trzeba ruszyć, i o propozycję diffu w oknie czatu.
@workspace Chcę pokryć regułę z docs/auth-rules.md: złe hasło
zostawia użytkownika na formularzu i pokazuje komunikat.
Wypisz pliki do zmiany i proponowany diff. Nie zapisuj niczego.Dostałem trzy wskazania. Jedno przyjąłem, dwa skreśliłem, i to są skreślenia z nazwanych kategorii, a nie z gatunku “model się nie postarał”.
Skreślenie pierwsze: zły plik. Propozycja dopisywała asercję na końcu istniejącego testu poprawnego hasła.
test('logowanie poprawnym hasłem wpuszcza na pulpit', async ({ page }) => {
const login = new LoginPage(page)
await login.open()
await login.signIn('uzytkownik@example.test', 'poprawne-haslo')
await expect(page.getByRole('heading', { name: 'Pulpit' })).toBeVisible()
+
+ await login.open()
+ await login.signIn('uzytkownik@example.test', 'zle-haslo')
+ await expect(page.getByText('Nieprawidłowy e-mail lub hasło')).toBeVisible()
})To jest najkrótsza droga do zielonego i najgorsza umowa o to, co ten test sprawdza. Scenariusz sukcesu w drugiej połowie zaczyna udawać scenariusz porażki, więc kiedy padnie, raport powie “logowanie poprawnym hasłem wpuszcza na pulpit” i skłamie w połowie przypadków. Przy dwóch zachowaniach w jednym teście tracę też najtańszą informację diagnostyczną, czyli to, które z nich się zepsuło. Model złożył to w jedną edycję nie dlatego, że tak jest lepiej, ale dlatego, że jedna edycja w jednym pliku jest najprostszą odpowiedzią na pytanie “gdzie to dodać”. Pytanie było moje i odpowiedź jest do niego dopasowana.
Skreślenie drugie: zmyślony export. W diffie dla page objectu pojawiła się jedna linia, której nie da się skompilować.
import { Locator, Page } from '@playwright/test'
+import { errorBanner } from './locators'
export class LoginPage {Pliku pages/locators.ts w tym repozytorium nie ma i nie było go na liście referencji nad odpowiedzią, więc model nie wyczytał tej nazwy z mojego kodu. Dopisał ją, bo w publicznym kodzie lokatory bardzo często mieszkają w osobnym module, a u mnie od sierpnia 2021 mieszkają w klasie strony. To jest dokładnie ta kategoria, którą policzyłem w kwietniowym notesie: sześć z dwudziestu dziewięciu poprawek i odrzuceń miało wtedy ten sam powód, czyli selektor albo identyfikator, którego w aplikacji nie ma. Szerszy kontekst nie zlikwidował tej klasy błędu. Przesunął ją o poziom wyżej, z jednej linii na plan dla trzech plików, i tym samym podniósł cenę wklejenia bez czytania.
Zostaje wskazanie trzecie i jest jedynym, które przyjąłem jako plan: nowy test w login.spec.ts plus metoda i lokator na page objectcie, przy docs/auth-rules.md nietkniętym, bo notatka jest źródłem oczekiwania, a nie miejscem na kod.
test('logowanie złym hasłem zostawia komunikat na formularzu', async ({ page }) => {
const login = new LoginPage(page)
await login.open()
await login.signIn('uzytkownik@example.test', 'zle-haslo')
await expect(login.error).toHaveText('Nieprawidłowy e-mail lub hasło')
await expect(login.submit).toBeVisible()
})Lokator komunikatu dopisałem jako getByRole('alert') i to nie wyszło z czatu. @workspace zindeksował moje testy i moją notatkę, nie zindeksował drzewa DOM aplikacji i nie wie, czy ten komunikat jest w roli alert, w aria-live, czy w toście, który znika po trzech sekundach. Sprawdziłem to jednym uruchomieniem przy otwartej stronie, bo reguła z zeszłego roku nie zmienia się przez szerszy kontekst: żaden nowy lokator nie wchodzi do page objectu bez takiego przebiegu.
Potem robię dwa przebiegi, nie jeden.
npx playwright test auth/login.spec.tsPierwszy pokazuje zielone. Drugi, po podmianie oczekiwanego tekstu na bzdurę, ma pokazać czerwone z czytelnym komunikatem, bo test, który nie umie paść, jest gorszy od braku testu. Werdykt wydaje tu Playwright 1.42.1, nie okno czatu, i to jest cała różnica między planem a dowodem.
Nie deleguję więc merge’a i nie jest to deklaracja światopoglądowa, tylko opis tego, kto ma w tej sprawie informacje. Z trzech wskazań dwa były do wyrzucenia, a to, które zostało, wymagało jeszcze jednej rzeczy spoza repozytorium, czyli spojrzenia na uruchomioną aplikację. Pod planem na trzy pliki podpisuje się dziś człowiek, który te trzy pliki przeczytał.
Devin jako nagłówek
12 marca, trzy dni przed tym wpisem, Cognition ogłosiło Devina i opisało go jako “AI software engineer”: agenta z własnym shellem, edytorem i przeglądarką, dostępnego na razie przez early access i zapisy. Nagłówki z tego tygodnia mówią, że zespoły właśnie dostały kolegę, który sam domyka zadanie.
Nie mam dostępu, nie instalowałem i nie zrobię z tego tutoriala o utrzymaniu testów. Odnotowuję datę, bo za rok będzie ważne, kiedy ta rozmowa się zaczęła, i jedną obserwację o kontraktach. Devin obiecuje wykonawcę, który dochodzi do końca. @workspace obiecuje czat, który widzi repozytorium. Pierwsza obietnica wymaga, żebym zaufał wynikowi, którego nie widziałem powstawać; druga wymaga, żebym przeczytał tekst i skreślił dwie trzecie. W marcu 2024 umiem wycenić tylko drugą i nie zamieniam jej na pierwszą dlatego, że pierwsza lepiej wygląda w nagłówku.
Claude 3 jako konkurent
4 marca Anthropic ogłosił rodzinę Claude 3: Opus i Sonnet od pierwszego dnia, Haiku jako zapowiedź, API dostępne bez listy oczekujących. To realny konkurent, tylko nie dla tego, o czym jest ten wpis.
Konkuruje z kartą w przeglądarce, czyli z miejscem, do którego wklejam kryteria akceptacji i z którego zabieram szkic listy przypadków. Nie konkuruje z @workspace w edytorze, bo nie ma dostępu do mojego repozytorium testów. Żeby odpowiedział na pytanie z tej sekcji wyżej, musiałbym mu wkleić page object, spec i notatkę, a potem przepisać odpowiedź z powrotem do plików, czyli wrócić do przełączania kontekstu, które czat w edytorze właśnie mi zdjął. To jest ta sama granica, którą stawiałem rok temu przy czacie w Insidersie, tylko po drugiej stronie stoi dziś mocniejszy model.
Nie przerzucam więc codziennej pracy na stronę Anthropica i nie pinuję tu Haiku jako czegoś, co mam pod ręką, bo w ogłoszeniu z 4 marca jest zapowiedziany, a nie wydany. Copilot zostaje w IDE przy kodzie, czat w przeglądarce zostaje przy szkicu. Piramida nie zmienia kształtu dlatego, że w marcu doszedł kolejny silnik; jej kształt zmienia decyzja o tym, co testuję i na którym poziomie, a tej decyzji żaden z tych modeli za mnie nie podejmuje.
Podsumowanie
Wieloplikowa pomoc, którą mam w marcu 2024, to kontekst repozytorium w Copilot Chat. Produkt o nazwie Copilot Workspace zostaje obietnicą z listopadowego Universe i nie mam o nim nic do napisania poza datą zapowiedzi.
Trzy zdania, które chcę móc przeczytać za rok. 15 marca nie otwieram Workspace i nie prowadzę zgłoszenia do pull requesta; otwieram czat w stabilnym VS Code, wołam @workspace i proszę o listę plików plus diff do przeczytania. Z trzech wskazań na syntetycznym logowaniu przyjąłem jedno, a dwa skreśliłem z powodów, które znam z jednej linii ghost textu: asercja doklejona do happy path i import z pliku, którego w repozytorium nie ma. Jedyny poprawny plan ułożyłem ja, po skreśleniach i po jednym przebiegu przy otwartej stronie, a dowód wydał runner, nie okno czatu.
Czego z tego nie wynika. Nie wynika, że Workspace nie wyjdzie w tym roku: GitHub zapowiedział 2024 i ja tę datę odnotowuję, nie negocjuję. Nie wynika, że Devin jest bez wartości, bo nie trzymałem go w rękach i nie mam jak tego zmierzyć. Nie wynika w końcu, że @workspace jest kosmetyką, bo pytanie o trzy pliki naraz faktycznie oszczędza mi kwadrans szukania po katalogach i tego kwadransa nie udaję.
W podsumowaniu 2023 zapisałem, po czym poznaję, że narzędzie weszło do zespołu, a nie do hype’u: ma nazwane zadanie, ma miejsce w procesie, ma swój wiersz w pipelinie albo świadomy powód, dla którego go nie ma, i ma człowieka odpowiedzialnego za wynik. @workspace ma dziś wszystkie cztery. Zadanie brzmi “pokaż, gdzie w tym repozytorium żyje ta reguła”, miejscem jest czat przy pisaniu testu, wiersza w pipelinie nie ma świadomie, a za wynik odpowiadam ja. Copilot Workspace nie przechodzi nawet pierwszego punktu, bo nie wiem jeszcze, co dokładnie robi.
Warunek, po którym poznam, że most od zgłoszenia przez plan do pull requesta dojrzał, formułuję tak: dostanę preview, które sam uruchomię, i to samo zgłoszenie w dwóch przebiegach da ten sam zestaw plików, bez importu z modułu, którego nie ma. Do tego czasu wieloplikowa pomoc kończy się u mnie na tekście w czacie. Diff wkleja i podpisuje człowiek.

