mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix(story-mode): name the files, say when a repository is new, and when a push stayed on this computer
From review round 9 (a newcomer to the project): - One or two files are named, not counted: "Claude writes .gitignore and saves the work", "Claude changes rename_by_date.sh.", "Claude writes the new files mathx.py and test_mathx.py." Three or more are counted. - The first save in a folder Claude turned into a repository says so: "Claude starts a git repository and saves the work: …". A `git init --bare` (the push target) does not count, nor a folder that already had commits. - A push to a repository in a folder on this computer (`To ./origin.git`) is a copy, not an upload: "then copies it to origin.git, a folder on this computer", and the final chip reads "copied to origin.git". - A tool lookup for plan mode reads "Claude gets ready to plan." Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a80638308b
commit
70b1ebcc19
16 files changed
+148
-67
No files matched your search
@@ -40,7 +40,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude writes 2 new files.",
|
||||
"story": "Claude writes the new files wordcount.py and test_basic.py.",
|
||||
"director": "Write ×2 · wordcount.py +23 · test_basic.py +21",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude changes 2 files.",
|
||||
"story": "Claude changes rename_by_date.sh and -dash.jpg.",
|
||||
"director": "Write · Edit ×3 · Bash · rename_by_date.sh +95 −3 · -dash.jpg +1",
|
||||
"balloon": "Now I'll create the samples and run the dry run.",
|
||||
"tests": null,
|
||||
@@ -205,7 +205,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude changes 1 file.",
|
||||
"story": "Claude changes rename_by_date.sh.",
|
||||
"director": "Edit ×3 · rename_by_date.sh +21 −6",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -231,7 +231,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude changes 1 file.",
|
||||
"story": "Claude changes README.md.",
|
||||
"director": "Edit ×3 · README.md +18 −2",
|
||||
"balloon": "All passing.",
|
||||
"tests": null,
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude writes 2 new files.",
|
||||
"story": "Claude writes the new files mathx.py and test_mathx.py.",
|
||||
"director": "Write ×2 · mathx.py +11 · test_mathx.py +26 · after 1 second opinion",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -65,7 +65,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work: \"add clamp() with pytest coverage\".",
|
||||
"story": "Claude starts a git repository and saves the work: \"add clamp() with pytest coverage\".",
|
||||
"director": "git commit -q -F -",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -167,7 +167,7 @@
|
||||
{
|
||||
"kind": "investigate",
|
||||
"ok": null,
|
||||
"story": "Claude gets the tools it needs ready.",
|
||||
"story": "Claude gets ready to plan.",
|
||||
"director": "ToolSearch",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -200,7 +200,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude changes 2 files.",
|
||||
"story": "Claude changes mathx.py and test_mathx.py.",
|
||||
"director": "Edit ×3 · mathx.py +11 · test_mathx.py +20 −1",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude writes 1 new file.",
|
||||
"story": "Claude writes the new file test_calc_stats.py.",
|
||||
"director": "Write · test_calc_stats.py +55",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -77,7 +77,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude writes 1 file and saves the work: \"add failing tests for calc_stats mean/median\".",
|
||||
"story": "Claude writes .gitignore and saves the work: \"add failing tests for calc_stats mean/median\".",
|
||||
"director": "git commit -q -F - · writes 1 file",
|
||||
"balloon": "Red as expected: collection error, exit code 2.",
|
||||
"tests": null,
|
||||
@@ -88,7 +88,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude writes 1 new file.",
|
||||
"story": "Claude writes the new file calc_stats.py.",
|
||||
"director": "Write · calc_stats.py +24",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -181,7 +181,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work and uploads it: \"add calc_stats.mode with smallest-value tie-breaking\".",
|
||||
"story": "Claude saves the work: \"add calc_stats.mode with smallest-value tie-breaking\", then copies it to origin.git, a folder on this computer.",
|
||||
"director": "git commit -q -F - · git push -u origin HEAD",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -243,7 +243,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude writes 1 file.",
|
||||
"story": "Claude writes NOTES.md.",
|
||||
"director": "Bash · NOTES.md +2",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -268,7 +268,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work and uploads it, but a step after that fails.",
|
||||
"story": "Claude saves the work and copies it to origin.git, a folder on this computer, but a step after that fails.",
|
||||
"director": "git commit -q -F - · git push · 1 error",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work: \"add slugify module with node:test suite\".",
|
||||
"story": "Claude starts a git repository and saves the work: \"add slugify module with node:test suite\".",
|
||||
"director": "git commit -q -F -",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude writes 1 new file.",
|
||||
"story": "Claude writes the new file README.md.",
|
||||
"director": "Write · README.md +79",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -123,7 +123,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work in 6 commits.",
|
||||
"story": "Claude starts a git repository and saves the work in 6 commits.",
|
||||
"director": "git commit -q -m \"chore: scaffold inventory package\" -m \"Ad…",
|
||||
"balloon": "Commits are in.",
|
||||
"tests": null,
|
||||
@@ -243,7 +243,7 @@
|
||||
{
|
||||
"kind": "fix",
|
||||
"ok": null,
|
||||
"story": "Claude changes 1 file.",
|
||||
"story": "Claude changes CHANGELOG.md.",
|
||||
"director": "Edit · CHANGELOG.md +2",
|
||||
"balloon": "Step 12:",
|
||||
"tests": null,
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude writes 1 file and saves the work: \"ignore Python and pytest caches\".",
|
||||
"story": "Claude writes .gitignore and saves the work: \"ignore Python and pytest caches\".",
|
||||
"director": "git commit -q -m \"chore: ignore Python and pytest caches · writes 1 file",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work and uploads it, but a step after that fails.",
|
||||
"story": "Claude starts a git repository, saves the work and copies it to remote.git, a folder on this computer, but a step after that fails.",
|
||||
"director": "git commit -q -F - · git push -u origin main · 1 error",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude writes 2 files and saves the work: \"add package.json and index.js with add and slugify\".",
|
||||
"story": "Claude starts a git repository, writes package.json and index.js, and saves the work: \"add package.json and index.js with add and slugify\".",
|
||||
"director": "git commit -q -m \"feat: add package.json and index.js with… · writes 2 files · after 1 second opinion",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
@@ -62,7 +62,7 @@
|
||||
{
|
||||
"kind": "check",
|
||||
"ok": true,
|
||||
"story": "Claude writes 1 file; the tests pass.",
|
||||
"story": "Claude writes index.test.mjs; the tests pass.",
|
||||
"director": "node --test index.test.mjs → ok · 8 passed · writes 1 file",
|
||||
"balloon": null,
|
||||
"tests": {
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
{
|
||||
"kind": "check",
|
||||
"ok": true,
|
||||
"story": "Claude writes 2 files; the tests pass, and Claude saves the work.",
|
||||
"story": "Claude writes test_csvstat.py and pyproject.toml; the tests pass, and Claude saves the work.",
|
||||
"director": "python3 -m pytest -q 2>&1 → ok ×2 · 17 passed · writes 2 files · 1 commit",
|
||||
"balloon": "Code and tests are committed.",
|
||||
"tests": {
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
{
|
||||
"kind": "save",
|
||||
"ok": true,
|
||||
"story": "Claude saves the work: \"add pomodoro timer with tests\".",
|
||||
"story": "Claude starts a git repository and saves the work: \"add pomodoro timer with tests\".",
|
||||
"director": "git commit -q -m \"feat: add pomodoro timer with tests\" -m \"… · then 1 second opinion",
|
||||
"balloon": null,
|
||||
"tests": null,
|
||||
|
||||
@@ -107,7 +107,7 @@ describe('Story Mode on real sessions', () => {
|
||||
|
||||
it('a commit at the end of a test one-liner is kept, and a background helper reports back in 9 s', () => {
|
||||
expect(panel('text-plan-dry-run-helper', 0, 'check')?.story).toBe(
|
||||
'Claude writes 2 files; the tests pass, and Claude saves the work.'
|
||||
'Claude writes test_csvstat.py and pyproject.toml; the tests pass, and Claude saves the work.'
|
||||
);
|
||||
expect(panel('text-plan-dry-run-helper', 0, 'plan')?.story).toBe('Claude writes a plan: 4 steps.');
|
||||
expect(pagesOf('text-plan-dry-run-helper')[1]).toMatchObject({ origin: 'task', durationMs: 9008 });
|
||||
@@ -125,7 +125,9 @@ describe('Story Mode on real sessions', () => {
|
||||
expect(panel('question-tests-first-push', 0, 'save')).toMatchObject({
|
||||
ok: true,
|
||||
// One commit, then a push in a command of its own: one commit, not two.
|
||||
story: 'Claude saves the work and uploads it, but a step after that fails.',
|
||||
// The folder had no repository: the first save starts one, and the reader is told.
|
||||
story:
|
||||
'Claude starts a git repository, saves the work and copies it to remote.git, a folder on this computer, but a step after that fails.',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -137,10 +139,10 @@ describe('Story Mode on real sessions', () => {
|
||||
roundsOk: [false, true],
|
||||
});
|
||||
expect(second.panels.find((p) => p.kind === 'save')?.story).toBe(
|
||||
'Claude saves the work and uploads it: "add calc_stats.mode with smallest-value tie-breaking".'
|
||||
'Claude saves the work: "add calc_stats.mode with smallest-value tie-breaking", then copies it to origin.git, a folder on this computer.'
|
||||
);
|
||||
expect(third.panels.find((p) => p.kind === 'save')?.story).toBe(
|
||||
'Claude saves the work and uploads it, but a step after that fails.'
|
||||
'Claude saves the work and copies it to origin.git, a folder on this computer, but a step after that fails.'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -184,10 +186,10 @@ describe('Story Mode on real sessions', () => {
|
||||
expect(pagesOf('background-helpers-reports')[0].finishedLater).toBe(2);
|
||||
});
|
||||
|
||||
it('a lookup of tools reads as getting them ready, without their names', () => {
|
||||
it('a lookup of tools reads as getting them ready, without their names; for plan mode, as getting ready to plan', () => {
|
||||
const panel = pagesOf('compact-then-plan-mode')
|
||||
.flatMap((p) => p.panels)
|
||||
.find((p) => p.story === 'Claude gets the tools it needs ready.');
|
||||
.find((p) => p.story === 'Claude gets ready to plan.');
|
||||
expect(panel).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
+23
-19
@@ -887,25 +887,29 @@ describe('story-mode.js overlay', () => {
|
||||
});
|
||||
|
||||
it('the final panel counts the commits of the whole turn, and says when the work was uploaded', async () => {
|
||||
boot(
|
||||
storyOf([
|
||||
prompt('ship it'),
|
||||
...edit('e1', '/work/demo-shop/a.py', ['+a = 1']),
|
||||
...bash('b1', 'pytest -q && git commit -qam "fix: a"', 'Test and commit', '3 passed in 0.1s'),
|
||||
...bash(
|
||||
'b2',
|
||||
'git commit -qam "docs: b" && git push',
|
||||
'Commit and push',
|
||||
'To ./origin.git\n 1a2b3c4..5d6e7f8 main -> main'
|
||||
),
|
||||
end(1000),
|
||||
])
|
||||
);
|
||||
app.openStoryMode(SID);
|
||||
await settle();
|
||||
expect($$('.story-panel[data-kind="complete"] .story-chips li').map((li) => li.textContent)).toContain(
|
||||
'2 commits, uploaded'
|
||||
);
|
||||
const chipsFor = async (to: string) => {
|
||||
boot(
|
||||
storyOf([
|
||||
prompt('ship it'),
|
||||
...edit('e1', '/work/demo-shop/a.py', ['+a = 1']),
|
||||
...bash('b1', 'pytest -q && git commit -qam "fix: a"', 'Test and commit', '3 passed in 0.1s'),
|
||||
...bash(
|
||||
'b2',
|
||||
'git commit -qam "docs: b" && git push',
|
||||
'Commit and push',
|
||||
`To ${to}\n 1a2b3c4..5d6e7f8 main -> main`
|
||||
),
|
||||
end(1000),
|
||||
])
|
||||
);
|
||||
app.openStoryMode(SID);
|
||||
await settle();
|
||||
return $$('.story-panel[data-kind="complete"] .story-chips li').map((li) => li.textContent);
|
||||
};
|
||||
expect(await chipsFor('git@github.com:me/shop.git')).toContain('2 commits, uploaded');
|
||||
app.closeStoryMode?.();
|
||||
// A folder on this computer: a copy, named.
|
||||
expect(await chipsFor('./origin.git')).toContain('2 commits, copied to origin.git');
|
||||
});
|
||||
|
||||
it('a chip that opened the drawer takes focus back, even when the page was redrawn meanwhile', async () => {
|
||||
|
||||
+35
-5
@@ -929,7 +929,7 @@ describe('buildStoryPage', () => {
|
||||
const check = page.panels.find((p) => p.kind === 'check')!;
|
||||
expect(check.caption.director).toBe('node --test → ok · 3 passed · writes 1 file · 1 commit');
|
||||
// The commit at the end of the test one-liner is not lost, nor the file written: the check says both.
|
||||
expect(check.caption.story).toBe('Claude writes 1 file; the tests pass, and Claude saves the work.');
|
||||
expect(check.caption.story).toBe('Claude writes index.js; the tests pass, and Claude saves the work.');
|
||||
});
|
||||
|
||||
it('one-line printf/echo writes and the files Claude Code says a command changed are edits', () => {
|
||||
@@ -962,20 +962,50 @@ describe('buildStoryPage', () => {
|
||||
|
||||
it('a commit then a push in its own command is one commit, named; a push alone only uploads', () => {
|
||||
const save = (lines: unknown[]) => pageOf(lines).panels.find((p) => p.kind === 'save')!.caption.story;
|
||||
expect(
|
||||
const ship = (to: string) =>
|
||||
save([
|
||||
prompt('ship it'),
|
||||
...bash('b1', 'git commit -qm "feat: add mode()"', 'Commit', ''),
|
||||
...bash('b2', 'git push origin main', 'Push', 'To ./origin.git\n f80bb31..3e0fb80 main -> main'),
|
||||
...bash('b2', 'git push origin main', 'Push', `To ${to}\n f80bb31..3e0fb80 main -> main`),
|
||||
end(1000),
|
||||
])
|
||||
).toBe('Claude saves the work and uploads it: "add mode()".');
|
||||
]);
|
||||
expect(ship('github.com:me/shop.git')).toBe('Claude saves the work and uploads it: "add mode()".');
|
||||
expect(ship('https://example.com/me/shop.git')).toBe('Claude saves the work and uploads it: "add mode()".');
|
||||
// A repository in a folder on this computer: a copy, and nothing left the machine.
|
||||
expect(ship('./origin.git')).toBe(
|
||||
'Claude saves the work: "add mode()", then copies it to origin.git, a folder on this computer.'
|
||||
);
|
||||
expect(ship('/srv/git/shop.git/')).toBe(
|
||||
'Claude saves the work: "add mode()", then copies it to shop.git, a folder on this computer.'
|
||||
);
|
||||
expect(save([prompt('push'), ...bash('b1', 'git push', 'Push', 'Everything up-to-date'), end(1000)])).toBe(
|
||||
'Claude uploads the work.'
|
||||
);
|
||||
expect(pageOf([prompt('c'), ...bash('b1', 'git commit -qm x', 'Commit', ''), end(1000)]).events[1].commits).toBe(1);
|
||||
});
|
||||
|
||||
it('the first save in a folder Claude made a repository says so; a bare copy target does not count', () => {
|
||||
const save = (lines: unknown[]) => pageOf(lines).panels.find((p) => p.kind === 'save')!.caption.story;
|
||||
expect(
|
||||
save([
|
||||
prompt('start'),
|
||||
...bash('b1', 'git init -q', 'Init', ''),
|
||||
...edit('e1', '/w/a.py', ['+a = 1']),
|
||||
...bash('b2', 'git add -A && git commit -qm "add a"', 'Commit', ''),
|
||||
end(1000),
|
||||
])
|
||||
).toBe('Claude starts a git repository and saves the work: "add a".');
|
||||
// `git init --bare` makes the push target, not the project's repository.
|
||||
expect(
|
||||
save([
|
||||
prompt('start'),
|
||||
...bash('b1', 'git init --bare ../origin.git', 'Remote', ''),
|
||||
...bash('b2', 'git commit -qm "add a"', 'Commit', ''),
|
||||
end(1000),
|
||||
])
|
||||
).toBe('Claude saves the work: "add a".');
|
||||
});
|
||||
|
||||
it('crafted agent text never takes seconds: every helper stays linear on its worst shapes', () => {
|
||||
// Each shape took one to several seconds before (a lazy regex rescanning per opener, or a
|
||||
// backtracking one over a long one-liner); a single call blocks the whole server.
|
||||
|
||||
Reference in new issue
Block a user