feat(L04): add Git status add and commit card
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
import assert from 'node:assert/strict'; import { access, readFile } from 'node:fs/promises'; import test from 'node:test';
|
||||
const source=JSON.parse(await readFile(new URL('../json/card_source.json',import.meta.url),'utf8'));
|
||||
test('L04 identity and task order are stable',()=>{assert.equal(source.card.number,'04');assert.equal(source.card.uuid,'ddcdbb08-80da-566e-a576-2e757848ae36');assert.deepEqual(source.tasks_order,['task01','task02','task03']);});
|
||||
test('worktree, index and commit evidence are explicit',()=>{const text=JSON.stringify(source);for(const token of ['worktree','index','status','commit','trace.log'])assert.match(text,new RegExp(token,'i'));});
|
||||
test('all task sources exist',async()=>{for(const name of ['task01_init_repository','task02_status_and_add','task03_first_commit'])await access(new URL(`../src/tasks/${name}.sh`,import.meta.url));});
|
||||
@@ -0,0 +1,67 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtemp, rm } from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { CardStateDatabase } from '../scripts/lib/card_state_db.mjs';
|
||||
|
||||
test('card state survives closing and reopening SQLite', async () => {
|
||||
const directory = await mkdtemp(path.join(os.tmpdir(), 'stem-card-state-'));
|
||||
const file = path.join(directory, 'state.sqlite3');
|
||||
const state = {
|
||||
schema: 'stem-card-navigation.v2',
|
||||
revision: 7,
|
||||
selected_at: '2026-07-17T12:00:00.000Z',
|
||||
snapshot_ref: 'task04.alloc5.cursor'
|
||||
};
|
||||
|
||||
try {
|
||||
const first = new CardStateDatabase(file);
|
||||
first.write('navigation', state);
|
||||
first.close();
|
||||
|
||||
const second = new CardStateDatabase(file);
|
||||
assert.deepEqual(second.read('navigation'), state);
|
||||
assert.equal(second.read('missing'), null);
|
||||
second.close();
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('audit log is append-only, ordered and keeps navigation timestamps', async () => {
|
||||
const directory = await mkdtemp(path.join(os.tmpdir(), 'stem-card-audit-'));
|
||||
const file = path.join(directory, 'state.sqlite3');
|
||||
try {
|
||||
const database = new CardStateDatabase(file);
|
||||
const navigation = {
|
||||
task: { id: 'task04' },
|
||||
block: { id: 'allocator-flow' },
|
||||
phase: { id: 'alloc5' },
|
||||
step: { id: 'alloc5-commit' },
|
||||
snapshot_ref: 'task04.alloc5.commit'
|
||||
};
|
||||
const first = database.appendEvent({
|
||||
eventType: 'navigation.activate',
|
||||
actor: 'nvim',
|
||||
navigation,
|
||||
occurredAt: '2026-07-17T12:00:00.000Z',
|
||||
payload: { source: 'F2' }
|
||||
});
|
||||
const second = database.appendEvent({
|
||||
eventType: 'checkpoint.ready',
|
||||
actor: 'nvim',
|
||||
navigation,
|
||||
occurredAt: '2026-07-17T12:00:01.000Z'
|
||||
});
|
||||
assert.equal(second.id, first.id + 1);
|
||||
assert.deepEqual(
|
||||
database.listEvents({ afterId: first.id, limit: 10 }).map(event => event.event_type),
|
||||
['checkpoint.ready']
|
||||
);
|
||||
assert.equal(database.listEvents()[0].snapshot_ref, navigation.snapshot_ref);
|
||||
database.close();
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
root="${CARD_ROOT:-$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)}"; task="${1:?task required}"
|
||||
case "$task" in task01_init_repository|task02_status_and_add|task03_first_commit) ;; *) exit 2 ;; esac
|
||||
exec env CARD_ROOT="$root" "$root/src/tasks/$task.sh"
|
||||
Reference in New Issue
Block a user