groundwork-method 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/bin/groundwork.js +86 -17
- package/dist/src/generators/system-test-runner/generator.js +52 -4
- package/dist/src/generators/system-test-runner/generator.js.map +1 -1
- package/package.json +1 -1
- package/src/docs/principles/design/usability-and-ux.md +11 -0
- package/src/docs/principles/foundations/testing.md +32 -6
- package/src/docs/principles/index.md +2 -1
- package/src/docs/principles/quality/observability.md +2 -2
- package/src/engineer-skills/groundwork-electron-engineer/SKILL.md +6 -1
- package/src/engineer-skills/groundwork-electron-engineer/references/documentation.md +126 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/performance-and-reliability.md +80 -0
- package/src/engineer-skills/groundwork-electron-engineer/references/testing-and-smoke.md +22 -0
- package/src/engineer-skills/groundwork-electron-engineer/sync-anchor.md +12 -4
- package/src/engineer-skills/groundwork-flutter-engineer/SKILL.md +7 -1
- package/src/engineer-skills/groundwork-flutter-engineer/references/documentation.md +122 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/observability.md +37 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/performance-and-reliability.md +100 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/security.md +96 -0
- package/src/engineer-skills/groundwork-flutter-engineer/references/testing.md +25 -0
- package/src/engineer-skills/groundwork-flutter-engineer/sync-anchor.md +13 -4
- package/src/engineer-skills/groundwork-go-engineer/SKILL.md +5 -2
- package/src/engineer-skills/groundwork-go-engineer/references/documentation.md +130 -0
- package/src/engineer-skills/groundwork-go-engineer/references/testing.md +63 -1
- package/src/engineer-skills/groundwork-go-engineer/sync-anchor.md +13 -4
- package/src/engineer-skills/groundwork-nextjs-engineer/SKILL.md +6 -1
- package/src/engineer-skills/groundwork-nextjs-engineer/references/accessibility.md +111 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/observability.md +48 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/security.md +131 -0
- package/src/engineer-skills/groundwork-nextjs-engineer/references/testing.md +59 -1
- package/src/engineer-skills/groundwork-nextjs-engineer/references/ux-principles.md +1 -49
- package/src/engineer-skills/groundwork-nextjs-engineer/sync-anchor.md +10 -3
- package/src/engineer-skills/groundwork-python-engineer/SKILL.md +5 -2
- package/src/engineer-skills/groundwork-python-engineer/references/security.md +148 -0
- package/src/engineer-skills/groundwork-python-engineer/references/testing.md +40 -1
- package/src/engineer-skills/groundwork-python-engineer/sync-anchor.md +11 -4
- package/src/generators/electron-app/docs/principles/stack/electron/index.md +2 -0
- package/src/generators/electron-app/files/tests/smoke/app.spec.ts.template +73 -8
- package/src/generators/flutter-app/docs/principles/stack/flutter/testing.md +14 -2
- package/src/generators/flutter-app/files/integration_test/app_test.dart.template +46 -12
- package/src/generators/go-microservice/docs/principles/stack/go/testing.md +17 -1
- package/src/generators/python-microservice/docs/principles/stack/python/testing.md +41 -0
- package/src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md +20 -0
- package/src/generators/system-test-runner/files/tests/system/test_render_smoke.py.template +30 -0
- package/src/generators/system-test-runner/generator.ts +58 -4
- package/src/generators/workspace-dev-cli/cli-src/dist/dev-bundle.js +1 -1
- package/src/hidden-skills/code-intelligence.md +6 -0
- package/src/hidden-skills/groundwork-architect/SKILL.md +1 -1
- package/src/hidden-skills/groundwork-architect/sync-anchor.md +2 -2
- package/src/hidden-skills/groundwork-bet/briefs/acceptance-auditor.md +68 -0
- package/src/hidden-skills/groundwork-bet/briefs/blind-reviewer.md +56 -0
- package/src/hidden-skills/groundwork-bet/briefs/coverage-auditor.md +95 -0
- package/src/hidden-skills/groundwork-bet/briefs/edge-case-tracer.md +64 -0
- package/src/hidden-skills/groundwork-bet/briefs/experience-auditor.md +83 -0
- package/src/hidden-skills/groundwork-bet/briefs/slice-worker.md +92 -26
- package/src/hidden-skills/groundwork-bet/instructions.md +4 -4
- package/src/hidden-skills/groundwork-bet/templates/bet-progress-test.md +16 -27
- package/src/hidden-skills/groundwork-bet/templates/change-proposal.md +1 -1
- package/src/hidden-skills/groundwork-bet/templates/decomposition/milestone-index.md +12 -16
- package/src/hidden-skills/groundwork-bet/templates/decomposition/slice.md +4 -8
- package/src/hidden-skills/groundwork-bet/templates/technical-design/03-api-design.md +1 -1
- package/src/hidden-skills/groundwork-bet/workflows/01-discovery.md +3 -1
- package/src/hidden-skills/groundwork-bet/workflows/02-design.md +11 -1
- package/src/hidden-skills/groundwork-bet/workflows/03-decomposition.md +60 -64
- package/src/hidden-skills/groundwork-bet/workflows/04-delivery.md +75 -42
- package/src/hidden-skills/groundwork-bet/workflows/05-validation.md +18 -7
- package/src/hidden-skills/groundwork-designer/sync-anchor.md +1 -1
- package/src/hidden-skills/groundwork-persona/instructions.md +11 -0
- package/src/hidden-skills/groundwork-review/checklists/implementation-readiness.md +1 -0
|
@@ -4,27 +4,35 @@ import { expect, test } from '@playwright/test';
|
|
|
4
4
|
import { _electron as electron } from 'playwright';
|
|
5
5
|
import type { AppStatus, CoreHealth } from '../../src/shared/ipc';
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// the
|
|
11
|
-
//
|
|
12
|
-
//
|
|
7
|
+
// Native UI check (the Electron side of NATIVE-CHECK-CONTRACT): the real built
|
|
8
|
+
// app launches and is driven through the contract dimensions — render, the
|
|
9
|
+
// named async states, and design-system token match — on the real binary. This
|
|
10
|
+
// is also the agent-closable boot loop the stack was chosen for. Keep it thin;
|
|
11
|
+
// rules are proven in the unit tiers and at the capability core's contracts,
|
|
12
|
+
// and the milestone's front-door bet-progress proof drives the real pipeline.
|
|
13
|
+
// Navigation / no-dead-ends is exempt while the app is single-screen; a bet
|
|
14
|
+
// that adds screens drives between them and back here. Runs under xvfb on Linux
|
|
15
|
+
// CI via the smoke target; tool/electron_exec.sh builds first and skips-with-
|
|
16
|
+
// reason when the Electron binary or a display server is unavailable.
|
|
13
17
|
|
|
14
18
|
const MAIN_ENTRY = path.join(__dirname, '..', '..', 'out', 'main', 'index.js');
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
function requireBuild(): void {
|
|
17
21
|
if (!fs.existsSync(MAIN_ENTRY)) {
|
|
18
22
|
throw new Error(
|
|
19
23
|
'out/main/index.js not found — run the build first (the smoke target does: npx nx run <%= fileName %>:smoke)',
|
|
20
24
|
);
|
|
21
25
|
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test('boots, renders on the design system, and answers an IPC round-trip', async () => {
|
|
29
|
+
requireBuild();
|
|
22
30
|
|
|
23
31
|
const app = await electron.launch({ args: [MAIN_ENTRY] });
|
|
24
32
|
try {
|
|
25
33
|
const page = await app.firstWindow();
|
|
26
34
|
|
|
27
|
-
//
|
|
35
|
+
// Render: the window booted and rendered app content.
|
|
28
36
|
await expect(page).toHaveTitle('<%= name %>');
|
|
29
37
|
await expect(page.getByRole('heading', { name: '<%= name %>' })).toBeVisible();
|
|
30
38
|
|
|
@@ -59,6 +67,37 @@ test('boots, renders, and answers an IPC round-trip', async () => {
|
|
|
59
67
|
/^(light|dark)$/,
|
|
60
68
|
);
|
|
61
69
|
|
|
70
|
+
// Token match: the design-system tokens resolved and landed in the render,
|
|
71
|
+
// rather than degrading to an unstyled default. The brand custom properties
|
|
72
|
+
// resolve on :root, and the heading paints with the projected primary token
|
|
73
|
+
// (text-primary → --color-primary → --gw-primary), not the browser default.
|
|
74
|
+
const tokens = await page.evaluate(() => {
|
|
75
|
+
// The renderer DOM globals live in the browser context; type them through
|
|
76
|
+
// a cast so the spec checks under the node tsconfig (no DOM lib), the same
|
|
77
|
+
// idiom the bridged-api evaluate above uses.
|
|
78
|
+
const dom = globalThis as unknown as {
|
|
79
|
+
getComputedStyle: (el: unknown) => {
|
|
80
|
+
getPropertyValue(prop: string): string;
|
|
81
|
+
color: string;
|
|
82
|
+
};
|
|
83
|
+
document: {
|
|
84
|
+
documentElement: unknown;
|
|
85
|
+
querySelector(sel: string): unknown;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const root = dom.getComputedStyle(dom.document.documentElement);
|
|
89
|
+
const heading = dom.document.querySelector('h1');
|
|
90
|
+
return {
|
|
91
|
+
primary: root.getPropertyValue('--gw-primary').trim(),
|
|
92
|
+
surface: root.getPropertyValue('--gw-surface').trim(),
|
|
93
|
+
headingColor: heading ? dom.getComputedStyle(heading).color : '',
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
expect(tokens.primary.length).toBeGreaterThan(0);
|
|
97
|
+
expect(tokens.surface.length).toBeGreaterThan(0);
|
|
98
|
+
expect(tokens.headingColor).not.toBe('');
|
|
99
|
+
expect(tokens.headingColor).not.toBe('rgb(0, 0, 0)');
|
|
100
|
+
|
|
62
101
|
// Main-process assertion via the driver's main-process hook.
|
|
63
102
|
const isPackaged = await app.evaluate(({ app: electronApp }) => electronApp.isPackaged);
|
|
64
103
|
expect(isPackaged).toBe(false);
|
|
@@ -66,3 +105,29 @@ test('boots, renders, and answers an IPC round-trip', async () => {
|
|
|
66
105
|
await app.close();
|
|
67
106
|
}
|
|
68
107
|
});
|
|
108
|
+
|
|
109
|
+
test('renders the unreachable core as a state, not a crash', async () => {
|
|
110
|
+
requireBuild();
|
|
111
|
+
|
|
112
|
+
// Named state, driven deterministically on the real binary: point the
|
|
113
|
+
// core-access seam at an unreachable address so the probe fails. The app must
|
|
114
|
+
// still render and show the user a designed "unreachable" state — a working
|
|
115
|
+
// app that cannot reach the core never shows a blank window or a crash.
|
|
116
|
+
const app = await electron.launch({
|
|
117
|
+
args: [MAIN_ENTRY],
|
|
118
|
+
env: { ...process.env, API_BASE_URL: 'http://127.0.0.1:1' },
|
|
119
|
+
});
|
|
120
|
+
try {
|
|
121
|
+
const page = await app.firstWindow();
|
|
122
|
+
|
|
123
|
+
// The shell still booted and rendered — the failure is a state, not a crash.
|
|
124
|
+
await expect(page.getByRole('heading', { name: '<%= name %>' })).toBeVisible();
|
|
125
|
+
|
|
126
|
+
// The core probe resolves to unreachable, and the renderer shows its state.
|
|
127
|
+
await expect(page.getByTestId('core-status')).toHaveText(
|
|
128
|
+
/Workspace core unreachable/,
|
|
129
|
+
);
|
|
130
|
+
} finally {
|
|
131
|
+
await app.close();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
@@ -8,7 +8,7 @@ last_reviewed: 2026-06-12
|
|
|
8
8
|
|
|
9
9
|
## TL;DR
|
|
10
10
|
|
|
11
|
-
Three tiers: pure-Dart unit tests with fakes, widget tests as the bulk of coverage, and `integration_test` happy paths on a headless Android emulator as the CI-canonical loop. Patrol enters only when a flow crosses the Flutter/OS boundary. Goldens run via alchemist. Surface tests assert wiring, rendering, and interaction — they never re-prove business logic already proven at the core's contract.
|
|
11
|
+
Three tiers: pure-Dart unit tests with fakes, widget tests as the bulk of coverage, and `integration_test` happy paths on a headless Android emulator as the CI-canonical loop. Patrol enters only when a flow crosses the Flutter/OS boundary. Goldens run via alchemist. Surface tests assert wiring, rendering, and interaction — they never re-prove business logic already proven at the core's contract. This is the Flutter idiom of the framework testing canon (`docs/principles/foundations/testing.md`) — widget tests are the honeycomb's fat middle, unit tests the thin solitary layer — and the canon wins on any disagreement.
|
|
12
12
|
|
|
13
13
|
## Why this matters
|
|
14
14
|
|
|
@@ -50,7 +50,9 @@ A small set of happy-path flows through the real app binary — launch, sign in
|
|
|
50
50
|
|
|
51
51
|
**Android is the CI gate; iOS is a local-only lane.** The headless Android emulator is cheap, scriptable, and agent-drivable; iOS simulators need macOS runners and routinely need hands — putting them in the gate trades the agent-closable loop for platform symmetry the wiring proof does not need. iOS-specific verification happens locally or on device farms (Firebase Test Lab, Codemagic) as an explicit, non-gating lane.
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
This tier is the Flutter side of the **native UI check contract** (`src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md`) — the `system-test-runner` drives it as the surface's visual gate, so it carries the contract's dimensions on the *real binary*: it renders without a blank or crash frame, drives the **named async states** (the unreachable/error path renders its designed card, not a red screen), and confirms a **design-system token landed** in the render (the status icon resolves the projected `StatusColors` token, not a flat default). Navigation / no-dead-ends is exempt while the app is single-screen; a bet that adds screens drives between them and back here. Each state is reached by faking the repository at the provider seam — you cannot summon a loading or unreachable state from a real backend on demand — while the milestone's front-door bet-progress proof drives the real gateway end to end.
|
|
54
|
+
|
|
55
|
+
Keep this tier thin. Every integration test is minutes of emulator time; if a widget test can carry the assertion, it does — the integration tier carries only what needs the real binary: render, the states, and token conformance.
|
|
54
56
|
|
|
55
57
|
### Patrol — only across the Flutter/OS boundary
|
|
56
58
|
|
|
@@ -60,6 +62,16 @@ Keep this tier thin. Every integration test is minutes of emulator time; if a wi
|
|
|
60
62
|
|
|
61
63
|
Goldens guard design-system-level components (the token-projected theme made visible). Use **alchemist**, with its platform-test vs CI-test split — CI variants render text as blocks, killing the cross-platform font flakiness that made goldens a deletion candidate. `golden_toolkit` is discontinued — legacy; migrate, do not adopt. Golden scope is the component library, not full screens: screen-level goldens churn on every copy change and teach the team to rubber-stamp diffs.
|
|
62
64
|
|
|
65
|
+
## Assertion-quality read-outs (and their stack limits)
|
|
66
|
+
|
|
67
|
+
The canon's quality read-outs are stack-dependent, and a client is honest about which apply:
|
|
68
|
+
|
|
69
|
+
- **Mutation testing** (the canon's read-out for whether assertions bite) has **no production-grade Dart tool** — the existing packages are experimental and unmaintained. The discipline it enforces is carried by review instead: fakes over mocks, assertions on semantics and visible text, and error/empty-state coverage. A hand-run experimental tool is a spot check on a dense pure-Dart algorithm, never a gate.
|
|
70
|
+
- **Property-based testing** applies narrowly: a dense pure-Dart unit with a real invariant — a mapper round-trip, a formatter, a validator that must never throw — can state the property with `glados` (the niche Dart option). It does not apply to widget trees.
|
|
71
|
+
- **Trace assertions and service-boundary fuzzing** (Schemathesis, coverage-guided fuzzing) are **N/A on the surface** — a Flutter client emits no OpenTelemetry spans, and the gateway's contract is fuzzed once at the capability core. Re-running either from the surface duplicates a proof that already exists.
|
|
72
|
+
|
|
73
|
+
Name tests by observable behaviour, not the widget under test: `'renaming updates the profile header'`, not `'ProfileView test'`.
|
|
74
|
+
|
|
63
75
|
## Legacy
|
|
64
76
|
|
|
65
77
|
`flutter_driver` (long deprecated for `integration_test`); `golden_toolkit`; Appium-first Flutter testing (a mixed-stack-org accommodation, not Flutter-native practice).
|
|
@@ -3,28 +3,62 @@ import 'package:flutter_test/flutter_test.dart';
|
|
|
3
3
|
import 'package:integration_test/integration_test.dart';
|
|
4
4
|
import 'package:<%= pubspecName %>/app.dart';
|
|
5
5
|
import 'package:<%= pubspecName %>/data/repositories/status_repository.dart';
|
|
6
|
+
import 'package:<%= pubspecName %>/domain/models/health_status.dart';
|
|
7
|
+
import 'package:<%= pubspecName %>/ui/core/theme/app_theme.dart';
|
|
6
8
|
|
|
7
9
|
import '../test/fakes/fake_status_repository.dart';
|
|
8
10
|
|
|
9
|
-
///
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
///
|
|
13
|
-
///
|
|
11
|
+
/// Native UI check (the Flutter side of NATIVE-CHECK-CONTRACT): the real app
|
|
12
|
+
/// binary launches on a headless emulator and is driven through the contract
|
|
13
|
+
/// dimensions — render, the named async states, and design-system token match.
|
|
14
|
+
/// Navigation / no-dead-ends is exempt while the app is single-screen; a bet
|
|
15
|
+
/// that adds screens extends this harness to drive between them and back.
|
|
16
|
+
///
|
|
17
|
+
/// Keep it thin — emulator minutes are the most expensive test currency in this
|
|
18
|
+
/// stack. The repository is faked at the provider seam so each state is reached
|
|
19
|
+
/// deterministically (you cannot summon a loading or unreachable state from a
|
|
20
|
+
/// real backend on demand); the milestone's front-door proof is what drives the
|
|
21
|
+
/// real gateway end to end (docs/principles/stack/flutter/testing.md).
|
|
14
22
|
void main() {
|
|
15
23
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ProviderScope(
|
|
20
|
-
overrides: [
|
|
21
|
-
statusRepositoryProvider.overrideWithValue(FakeStatusRepository()),
|
|
22
|
-
],
|
|
25
|
+
Widget bootWith(StatusRepository repo) => ProviderScope(
|
|
26
|
+
overrides: [statusRepositoryProvider.overrideWithValue(repo)],
|
|
23
27
|
child: const App(),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
testWidgets('renders the reachable state on the design system',
|
|
31
|
+
(tester) async {
|
|
32
|
+
await tester.pumpWidget(bootWith(FakeStatusRepository()));
|
|
33
|
+
await tester.pumpAndSettle();
|
|
34
|
+
|
|
35
|
+
// Render: the home view reached its data state — not a blank or error frame.
|
|
36
|
+
expect(find.text('Wired to the workspace gateway'), findsOneWidget);
|
|
37
|
+
|
|
38
|
+
// Token match: the status icon resolves the projected design-system token
|
|
39
|
+
// (StatusColors.success), in whichever theme the platform launched — proof
|
|
40
|
+
// the design system landed in the real render rather than a flat default.
|
|
41
|
+
final icon = tester.widget<Icon>(find.byIcon(Icons.check_circle_outline));
|
|
42
|
+
expect(
|
|
43
|
+
icon.color,
|
|
44
|
+
anyOf(
|
|
45
|
+
buildLightTheme().extension<StatusColors>()!.success,
|
|
46
|
+
buildDarkTheme().extension<StatusColors>()!.success,
|
|
24
47
|
),
|
|
25
48
|
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
testWidgets('renders the unreachable state as a state, not a crash',
|
|
52
|
+
(tester) async {
|
|
53
|
+
await tester.pumpWidget(
|
|
54
|
+
bootWith(FakeStatusRepository(result: const HealthStatus.unreachable())),
|
|
55
|
+
);
|
|
26
56
|
await tester.pumpAndSettle();
|
|
27
57
|
|
|
28
|
-
|
|
58
|
+
// Named state: the unreachable path renders its designed card with no
|
|
59
|
+
// uncaught exception — a working app that cannot reach the core still shows
|
|
60
|
+
// the user a state, never a blank or a red screen of death.
|
|
61
|
+
expect(find.text('Gateway unreachable'), findsOneWidget);
|
|
62
|
+
expect(tester.takeException(), isNull);
|
|
29
63
|
});
|
|
30
64
|
}
|
|
@@ -8,7 +8,7 @@ last_reviewed: 2026-05-26
|
|
|
8
8
|
|
|
9
9
|
## TL;DR
|
|
10
10
|
|
|
11
|
-
Service perimeter tests are our default: real Postgres via Testcontainers, real HTTP via `httptest`, no mocks inside the service boundary. Unit tests exist for isolated, complex logic only. The test
|
|
11
|
+
Service perimeter tests are our default: real Postgres via Testcontainers, real HTTP via `httptest`, no mocks inside the service boundary. Unit tests exist for isolated, complex logic only. The default shape is the **test honeycomb** — a fat middle of sociable service tests, a thin solitary-unit layer, a few end-to-end checks — not the pyramid, which assumes a database in a test is expensive. This is the Go idiom of the framework testing canon (`docs/principles/foundations/testing.md`); the canon is the parent principle and wins on any disagreement.
|
|
12
12
|
|
|
13
13
|
## Why this matters
|
|
14
14
|
|
|
@@ -143,6 +143,22 @@ go test ./internal/meetings/...
|
|
|
143
143
|
go test -v -run TestCreateMeeting ./internal/meetings/...
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
+
## Trace assertions — observability is a test surface
|
|
147
|
+
|
|
148
|
+
A critical-path request must emit an unbroken trace; a missing span is a test failure, not an instrumentation TODO. The mechanism is the OTel SDK's **in-memory span exporter** — register one in the test process, exercise the handler, and assert on the finished spans (the entry span exists, the work span is its descendant, the attributes a dashboard query depends on are present). Use `go.opentelemetry.io/otel/sdk/trace/tracetest` (`NewInMemoryExporter`), no external tooling. Assert what the contract promises and let the rest float — pinning the whole span tree couples the test to implementation.
|
|
149
|
+
|
|
150
|
+
## Mutation testing — the assertion-quality read-out
|
|
151
|
+
|
|
152
|
+
A fat service-perimeter test drives many branches through one HTTP call and can *execute* them all while only asserting on the response body. Mutation testing is the instrument that proves the suite checks what it runs: inject a fault, confirm a test fails; a surviving mutant is a covered-but-unchecked line. It is a **signal, never a gate**. Go's tooling is immature (`gremlins` is pre-1.0 and slow; `go-mutesting` is unmaintained), so here it stays a hand-run spot check on a dense package under active change, not a CI expectation.
|
|
153
|
+
|
|
154
|
+
## Generate the inputs you can't enumerate
|
|
155
|
+
|
|
156
|
+
Example-based tests check the cases you thought of; the bugs live in the cases you didn't. For dense, boundary-poor logic with a real invariant — a round-trip, a parser that must never panic, a calculation with an algebraic law — state the property and let the framework generate and shrink counterexamples (`pgregory.net/rapid`, or stdlib `testing/quick`). At the byte boundary, native `go test -fuzz` is first-class for parsers and decoders, and a failing input is saved under `testdata/fuzz/` as a permanent regression seed. Reach for these where invariants are real, not everywhere.
|
|
157
|
+
|
|
158
|
+
## Naming
|
|
159
|
+
|
|
160
|
+
Name a test by behaviour, not implementation: `[Function] should [expected outcome] when [condition]`. `TestCreateItem_Success` conveys nothing the dashboard does not already show; a failing name should let an on-call engineer form a hypothesis without opening the file.
|
|
161
|
+
|
|
146
162
|
## Anti-patterns
|
|
147
163
|
|
|
148
164
|
- **Mocking the database.** The whole point is to test the repository against a real schema.
|
|
@@ -27,6 +27,8 @@ The **honeycomb model** inverts the priority:
|
|
|
27
27
|
|
|
28
28
|
This gives us a suite where a passing run is a meaningful signal. The bugs we care about — boundary mismatches, SQL correctness, serialisation errors, provider contract violations — are caught before they reach production.
|
|
29
29
|
|
|
30
|
+
This is the Python idiom of the framework testing canon (`docs/principles/foundations/testing.md`); the canon is the parent principle and wins on any disagreement.
|
|
31
|
+
|
|
30
32
|
---
|
|
31
33
|
|
|
32
34
|
## The Three Tiers
|
|
@@ -256,6 +258,45 @@ A failing test name should give an on-call engineer enough information to form a
|
|
|
256
258
|
|
|
257
259
|
---
|
|
258
260
|
|
|
261
|
+
## Trace Assertions — observability is a test surface
|
|
262
|
+
|
|
263
|
+
A critical-path request must emit an unbroken trace; a missing span is a test failure, not an instrumentation TODO. Register an OTel **`InMemorySpanExporter`** in the test process, exercise the endpoint, and assert on the finished spans — the entry span exists, the trace stays connected across the service hop, and the attributes a dashboard query depends on are present.
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
267
|
+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
268
|
+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
|
|
269
|
+
|
|
270
|
+
@pytest.fixture
|
|
271
|
+
def spans():
|
|
272
|
+
exporter = InMemorySpanExporter()
|
|
273
|
+
provider = TracerProvider()
|
|
274
|
+
provider.add_span_processor(SimpleSpanProcessor(exporter))
|
|
275
|
+
# install provider for the test, exercise the system, then:
|
|
276
|
+
return exporter
|
|
277
|
+
|
|
278
|
+
async def test_transcribe_emits_connected_trace(client, spans):
|
|
279
|
+
await client.post("/transcribe", json={"audio_url": "gs://b/f.mp3"})
|
|
280
|
+
names = [s.name for s in spans.get_finished_spans()]
|
|
281
|
+
assert "POST /transcribe" in names
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Assert the spans the contract promises and let the rest float — pinning the whole tree couples the test to implementation.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Mutation Testing — the assertion-quality read-out
|
|
289
|
+
|
|
290
|
+
A service test drives many branches through one HTTP call and can *execute* them all while only asserting on the response body. Mutation testing proves the suite checks what it runs: inject a fault, confirm a test fails; a surviving mutant is a covered-but-unchecked line. Python's tooling is production-grade — **`mutmut`** and **`cosmic-ray`** — but it is expensive, so it is a **signal, never a gate**: run it incrementally on the high-risk modules the risk matrix flags and on changed code only. A surviving mutant on changed high-risk code is the missing assertion to add — the same read-out catches AI-generated tests whose oracle was lifted from the implementation. (Note: `mutmut` 3 no longer mutates module-level code.)
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Generate the Inputs You Can't Enumerate
|
|
295
|
+
|
|
296
|
+
Example-based tests check the cases you thought of; the bugs live in the cases you didn't. For pure logic with a real invariant — a round-trip (`decode ∘ encode = id`), a parser that must never raise, a domain calculation with an algebraic law — state the property and let **`Hypothesis`** generate and shrink counterexamples. One property covers an infinity of examples, and most caught faults surface on a single generated input. At the API boundary, **Schemathesis** derives a semantics-aware fuzzer straight from the OpenAPI schema and finds materially more defects than example-based API tests for the cost of pointing it at the spec. Reach for these where invariants are real; the authoring cost (a meaningful property needs a generator) is why they are not everywhere.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
259
300
|
## Running Tests
|
|
260
301
|
|
|
261
302
|
```bash
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Native UI Check Contract
|
|
2
|
+
|
|
3
|
+
This is the contract a platform's UI check must satisfy to verify a `graphical-ui` surface. It exists because a graphical surface that ships with no UI check is unverified — and a check that cannot run must **block the milestone, not silently skip it**. When the `system-test-runner` generator meets a graphical surface whose test medium it has no runner for, it emits a fail-closed placeholder (`tests/system/test_<surface>_ui_check_missing.py`) that fails with the named gap. Implementing a real check to this contract is what turns that red green.
|
|
4
|
+
|
|
5
|
+
GroundWork ships **conforming** checks for three mediums today: `playwright` (web, via the runner's own `tests/system/` suite) and `flutter-integration` / `playwright-electron` (the Flutter `integration_test` and Electron `_electron` smoke harnesses the app scaffolds ship — each driven by the `system-test-runner` and each carrying the contract's dimensions on the real binary: render, the named async states, and design-system token match; navigation is exempt while those scaffolds are single-screen). A graphical surface on any other platform — a native iOS/SwiftUI app, an Android-native app, a desktop-native shell — needs a check built to the contract below, registered under a new test medium the generator recognises; until then the surface gets the fail-closed placeholder.
|
|
6
|
+
|
|
7
|
+
## What a native UI check must cover
|
|
8
|
+
|
|
9
|
+
A conforming check drives the **running, shipping build** of the surface — the artifact a user actually launches, not a test target that runs code the shipping build omits — and verifies, across the surface's key screens:
|
|
10
|
+
|
|
11
|
+
1. **Render.** Each key screen renders without a blank frame, an unstyled fallback, an error-boundary/crash overlay, or an uncaught exception. The screen a user reaches shows what it is supposed to show.
|
|
12
|
+
2. **Navigation — no dead ends.** Every screen the surface reaches has a way back or onward; no flow strands the user with no exit. The check drives the real navigation between the surface's screens and confirms each is reachable and leavable. A single-screen surface is exempt (there is nowhere to strand the user) — the same guard the web render-smoke applies when there is only one route; a surface that adds a second screen owes the navigation assertion.
|
|
13
|
+
3. **The named states.** For every asynchronous view, the check exercises its full set of states — empty, loading, in-progress, error — and confirms each renders as designed rather than as a frozen or broken screen. A view that only renders when data arrives is incomplete.
|
|
14
|
+
4. **Design-system match.** The surface renders in the project's design system — the specified tokens (colour, type, spacing, elevation, motion) resolve and land, rather than degrading to a flat platform default.
|
|
15
|
+
|
|
16
|
+
## How it integrates
|
|
17
|
+
|
|
18
|
+
- Register the new test medium so `system-test-runner`'s `KNOWN_MEDIA` set recognises it, and wire the runner fixture to drive the platform's harness (the pattern `flutter-integration` and `playwright-electron` already follow: the surface ships its harness, the fixture drives it through the app's build/test command as a subprocess).
|
|
19
|
+
- The check is part of the permanent system suite (`tests/system/`), run on every milestone close and at validation — the same fail-closed gate the web `render_smoke` / `a11y_smoke` / `token_conformance` checks run under.
|
|
20
|
+
- Until a platform's check exists, the placeholder stands in and fails. That is the correct state: the milestone cannot be declared proven on a surface nothing checks.
|
|
@@ -111,6 +111,22 @@ def _render_smoke(page: Page, surface_slug: str, base_url: str | None) -> None:
|
|
|
111
111
|
f"({metrics['nodes']} nodes, {metrics['text']} chars of text)"
|
|
112
112
|
)
|
|
113
113
|
|
|
114
|
+
# 6. Not a dead end. When the app has more than one route, every
|
|
115
|
+
# screen offers a same-origin way onward or back, so a user is
|
|
116
|
+
# never stranded (the "you can reach the library but never leave
|
|
117
|
+
# it" class). Single-route apps are exempt.
|
|
118
|
+
if len(ROUTES) > 1:
|
|
119
|
+
nav_links = page.evaluate(
|
|
120
|
+
"() => Array.from(document.querySelectorAll('a[href]'))"
|
|
121
|
+
".filter(a => { const h = a.getAttribute('href') || '';"
|
|
122
|
+
" return h.startsWith('/') || h.startsWith(location.origin);"
|
|
123
|
+
" }).length"
|
|
124
|
+
)
|
|
125
|
+
assert nav_links > 0, (
|
|
126
|
+
f"{ctx}: dead-end screen — no in-app navigation link to "
|
|
127
|
+
f"leave this route"
|
|
128
|
+
)
|
|
129
|
+
|
|
114
130
|
# On pass, persist the screenshot for Tiers 2-3 to read.
|
|
115
131
|
out = _VISUAL_DIR / surface_slug
|
|
116
132
|
out.mkdir(parents=True, exist_ok=True)
|
|
@@ -216,6 +232,20 @@ def _render_smoke(page: Page, surface_slug: str) -> None:
|
|
|
216
232
|
f"({metrics['nodes']} nodes, {metrics['text']} chars of text)"
|
|
217
233
|
)
|
|
218
234
|
|
|
235
|
+
# Not a dead end: a multi-route surface offers a same-origin way
|
|
236
|
+
# off every screen, so a user is never stranded.
|
|
237
|
+
if len(ROUTES) > 1:
|
|
238
|
+
nav_links = page.evaluate(
|
|
239
|
+
"() => Array.from(document.querySelectorAll('a[href]'))"
|
|
240
|
+
".filter(a => { const h = a.getAttribute('href') || '';"
|
|
241
|
+
" return h.startsWith('/') || h.startsWith(location.origin);"
|
|
242
|
+
" }).length"
|
|
243
|
+
)
|
|
244
|
+
assert nav_links > 0, (
|
|
245
|
+
f"{ctx}: dead-end screen — no in-app navigation link to "
|
|
246
|
+
f"leave this route"
|
|
247
|
+
)
|
|
248
|
+
|
|
219
249
|
out = _VISUAL_DIR / surface_slug
|
|
220
250
|
out.mkdir(parents=True, exist_ok=True)
|
|
221
251
|
slug = route.strip("/").replace("/", "_") or "root"
|
|
@@ -73,6 +73,31 @@ function parseSurfaces(
|
|
|
73
73
|
}));
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** A fail-closed pytest stub for a graphical surface with no runnable UI check.
|
|
77
|
+
* It fails (never skips) so the surface's UI proof is an honest red until a
|
|
78
|
+
* platform check is implemented per NATIVE-CHECK-CONTRACT.md. Deleting it to go
|
|
79
|
+
* green is the silent-skip this exists to prevent. */
|
|
80
|
+
function uiCheckPlaceholder(s: SurfaceTemplateSpec): string {
|
|
81
|
+
return `import pytest
|
|
82
|
+
|
|
83
|
+
# AUTO-GENERATED fail-closed placeholder — do not delete to go green.
|
|
84
|
+
# Surface "${s.slug}" (test medium "${s.medium}") is a surface GroundWork has no
|
|
85
|
+
# UI check runner for. A milestone cannot be proven on a surface nothing checks,
|
|
86
|
+
# so this placeholder FAILS until a platform UI check is implemented for it per
|
|
87
|
+
# src/generators/system-test-runner/NATIVE-CHECK-CONTRACT.md (render,
|
|
88
|
+
# navigation / no dead ends, the named states, design-system token match).
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_${s.ident}_ui_check_not_implemented():
|
|
92
|
+
pytest.fail(
|
|
93
|
+
"No UI check runner for surface '${s.slug}' (medium '${s.medium}'). "
|
|
94
|
+
"Implement a platform UI check per "
|
|
95
|
+
"system-test-runner/NATIVE-CHECK-CONTRACT.md, then replace this "
|
|
96
|
+
"fail-closed placeholder. A graphical surface must not ship unverified."
|
|
97
|
+
)
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
|
|
76
101
|
export async function systemTestRunnerGenerator(
|
|
77
102
|
tree: Tree,
|
|
78
103
|
options: SystemTestRunnerGeneratorSchema
|
|
@@ -92,6 +117,20 @@ export async function systemTestRunnerGenerator(
|
|
|
92
117
|
const flutterSurfaces = (surfaces ?? []).filter((s) => s.medium === 'flutter-integration');
|
|
93
118
|
const electronSurfaces = (surfaces ?? []).filter((s) => s.medium === 'playwright-electron');
|
|
94
119
|
|
|
120
|
+
// Every test medium GroundWork knows how to run a check for. A graphical
|
|
121
|
+
// surface registered with a medium outside this set has no UI check runner —
|
|
122
|
+
// and a milestone cannot be proven on a surface nothing checks. We refuse to
|
|
123
|
+
// silently leave it unverified: each such surface gets a fail-closed
|
|
124
|
+
// placeholder check (below) naming the gap, never a silent no-op.
|
|
125
|
+
const KNOWN_MEDIA = new Set([
|
|
126
|
+
'playwright',
|
|
127
|
+
'subprocess-cli',
|
|
128
|
+
'protocol-client',
|
|
129
|
+
'flutter-integration',
|
|
130
|
+
'playwright-electron',
|
|
131
|
+
]);
|
|
132
|
+
const unsupportedSurfaces = (surfaces ?? []).filter((s) => !KNOWN_MEDIA.has(s.medium));
|
|
133
|
+
|
|
95
134
|
// Playwright structure follows graphical surfaces: any playwright surface in
|
|
96
135
|
// registry mode, the graphical-ui value in single-medium mode. pexpect ships
|
|
97
136
|
// alongside the subprocess runners so interactive (REPL) CLI flows are testable.
|
|
@@ -121,10 +160,13 @@ export async function systemTestRunnerGenerator(
|
|
|
121
160
|
}
|
|
122
161
|
);
|
|
123
162
|
|
|
124
|
-
// Playwright structure ships only with a graphical surface: the
|
|
125
|
-
// package, the axe-core a11y smoke, and the render-smoke gate
|
|
126
|
-
// pytest-playwright, which the pyproject template declares only when
|
|
127
|
-
// includePlaywright is set.
|
|
163
|
+
// Playwright structure ships only with a graphical web surface: the
|
|
164
|
+
// page-object package, the axe-core a11y smoke, and the render-smoke gate
|
|
165
|
+
// depend on pytest-playwright, which the pyproject template declares only when
|
|
166
|
+
// includePlaywright is set. Removing the web-specific gates here is correct —
|
|
167
|
+
// they genuinely cannot run without a web surface. What must never happen is a
|
|
168
|
+
// graphical surface left with no check at all; the placeholder below closes
|
|
169
|
+
// that gap fail-closed instead of silently.
|
|
128
170
|
if (!includePlaywright) {
|
|
129
171
|
tree.delete('tests/system/pages');
|
|
130
172
|
tree.delete('tests/system/test_a11y_smoke.py');
|
|
@@ -134,6 +176,18 @@ export async function systemTestRunnerGenerator(
|
|
|
134
176
|
tree.delete('tests/system/test_token_conformance.py');
|
|
135
177
|
}
|
|
136
178
|
|
|
179
|
+
// Fail-closed: a graphical surface whose test medium GroundWork cannot run a
|
|
180
|
+
// check for gets a placeholder that FAILS naming the gap, never a silent skip.
|
|
181
|
+
// The scaffold still generates and its other tests run; this surface's UI
|
|
182
|
+
// proof is an honest red until a platform check is implemented per
|
|
183
|
+
// NATIVE-CHECK-CONTRACT.md — the follow-on that turns it green.
|
|
184
|
+
for (const s of unsupportedSurfaces) {
|
|
185
|
+
tree.write(
|
|
186
|
+
`tests/system/test_${s.ident}_ui_check_missing.py`,
|
|
187
|
+
uiCheckPlaceholder(s)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
137
191
|
await formatFiles(tree);
|
|
138
192
|
|
|
139
193
|
recordGeneratorProvenance(tree, 'system-test-runner', options as unknown as Record<string, unknown>);
|
|
@@ -877,7 +877,7 @@ var path6 = __toESM(require("path"));
|
|
|
877
877
|
// src/generators/workspace-dev-cli/cli-src/src/util/version.ts
|
|
878
878
|
var fs6 = __toESM(require("fs"));
|
|
879
879
|
var path5 = __toESM(require("path"));
|
|
880
|
-
var DEV_CLI_VERSION = "0.
|
|
880
|
+
var DEV_CLI_VERSION = "0.11.0";
|
|
881
881
|
function stampedFrameworkVersion() {
|
|
882
882
|
try {
|
|
883
883
|
const state = JSON.parse(
|
|
@@ -127,3 +127,9 @@ with ordinary file edits. No repo map for a language — not built in and not ye
|
|
|
127
127
|
`unmapped` list): either enable it (above) or infer the missing structure from targeted reads
|
|
128
128
|
(entry points, manifests, imports) in the same shape. The downstream contract is identical — only
|
|
129
129
|
the means differ. Say so rather than implying structural coverage you did not have.
|
|
130
|
+
|
|
131
|
+
In a **git worktree** (e.g. a bet under delivery): the map cache is per-working-tree, so build it
|
|
132
|
+
in the worktree before relying on it (`npx groundwork-method repo-map`). Serena is registered with
|
|
133
|
+
`--project .`, which resolves to the session root rather than the worktree path — treat its symbol
|
|
134
|
+
tools as best-effort there and lean on the freshly-built map, falling back to ordinary reads under
|
|
135
|
+
the same contract.
|
|
@@ -28,7 +28,7 @@ Durable architectural guidance lives in `references/`. This skill decides what t
|
|
|
28
28
|
2. Contracts are the single source of truth — specs are authored, clients and tests derived.
|
|
29
29
|
3. Reliability and security are designed in from the first boundary, never patched on.
|
|
30
30
|
4. Core-and-edges structure: dependencies point inward toward a core that imports nothing concrete.
|
|
31
|
-
5. We
|
|
31
|
+
5. We prove software by using the real thing the way its user does — boundaries are chosen so each is provable against real dependencies through the front door, not behind a mock.
|
|
32
32
|
6. Decisions are recorded and governed — context, assumptions, and trade-offs, with an owner and a review trigger — so they can be re-evaluated when their assumptions break. The record is immutable; the decision is not.
|
|
33
33
|
7. Agents are first-class consumers — every interface is designed to be machine-consumable.
|
|
34
34
|
|
|
@@ -17,7 +17,7 @@ a review of the matching reference so the distillation never drifts.
|
|
|
17
17
|
| src/docs/principles/system-design/data-engineering.md | fd0df432fc96d51c52e6ad87bd0159fa7eac7840e669fbb4174a2b6a68ae331d | 2026-06-19 |
|
|
18
18
|
| src/docs/principles/quality/reliability.md | 9c9788504e0963458667d2727c3fc2359776108be593a2efc6603f6470002252 | 2026-06-19 |
|
|
19
19
|
| src/docs/principles/quality/performance.md | 18b6d3391c57d97342068f9f1da732b24de4221489d0459bb6ad8900fac0a02e | 2026-06-19 |
|
|
20
|
-
| src/docs/principles/quality/observability.md |
|
|
20
|
+
| src/docs/principles/quality/observability.md | 8aa60e213ba03e989c93263153e3a1ac10b2336f6d0360c394f473660d565a0b | 2026-06-26 |
|
|
21
21
|
| src/docs/principles/quality/security.md | 61157d97677142737ec537954dc5aaad7a04012cc8a3dcc855e2d324287fdc64 | 2026-06-19 |
|
|
22
22
|
| src/docs/principles/quality/privacy.md | d84f6bed50169b40daeb2a0ec7082dbd12d91d3abfa304b169cb9eb3fab494fb | 2026-06-19 |
|
|
23
23
|
| src/docs/principles/delivery/platform.md | 3cbf6c13298bf1c148278ae26acdbc2601a06615ff8d85cdb0de3b41c008c626 | 2026-06-19 |
|
|
@@ -31,4 +31,4 @@ a review of the matching reference so the distillation never drifts.
|
|
|
31
31
|
| src/docs/principles/system-design/surface-architecture.md | 724e2183433b0db8d54466deffc0be877d847cdb6b61f0da9060491907151b91 | 2026-06-19 |
|
|
32
32
|
| src/docs/principles/system-design/identity-and-access.md | 18c99f755a37bec69de595a9784171c88639845c13c2f5a8497b55e40c3a5edf | 2026-06-19 |
|
|
33
33
|
| src/docs/principles/system-design/durable-execution.md | e4faad5864bcbecb80c79983be6a941fee652f2f78b38701dd8bd2dda47c3ec3 | 2026-06-19 |
|
|
34
|
-
| src/docs/principles/index.md |
|
|
34
|
+
| src/docs/principles/index.md | 86e957ef6437b4ef551a67cb66f1e30aef971716636181bce5f5996f701323c6 | 2026-06-27 |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: acceptance-auditor
|
|
3
|
+
description: >
|
|
4
|
+
Verifies a slice diff does what the design says and nothing more, and does it
|
|
5
|
+
honestly. One of four independent review lenses the Delivery driver dispatches per
|
|
6
|
+
slice (groundwork-bet/workflows/04-delivery.md, Step 2); only the report flows back.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Acceptance Auditor
|
|
10
|
+
|
|
11
|
+
## How This Brief Is Invoked
|
|
12
|
+
|
|
13
|
+
This brief runs in an **isolated subagent context** (Protocol 9 mechanics), dispatched
|
|
14
|
+
by the Delivery driver during the slice review, in parallel with the blind reviewer, the
|
|
15
|
+
edge-case tracer, and the coverage auditor. It is **not** the slice-worker that wrote the
|
|
16
|
+
diff. Only the report flows back to the driver.
|
|
17
|
+
|
|
18
|
+
This is the only lens that holds the diff against the approved design. It judges
|
|
19
|
+
**conformance and honesty**: does the implementation deliver the slice's Required
|
|
20
|
+
Capabilities, only those, and for the right reason — not by gaming the test.
|
|
21
|
+
|
|
22
|
+
## Inputs
|
|
23
|
+
|
|
24
|
+
The driver passes:
|
|
25
|
+
|
|
26
|
+
- The slice's **uncommitted diff**.
|
|
27
|
+
- The slice's **Required Capabilities** (its Scope, from the slice file under
|
|
28
|
+
`docs/bets/<bet-slug>/decomposition/`).
|
|
29
|
+
- The prose **API and data design** — `technical-design/03-api-design.md` and
|
|
30
|
+
`04-data-design.md` — the shapes the implementation must match.
|
|
31
|
+
|
|
32
|
+
## The work
|
|
33
|
+
|
|
34
|
+
Verify the implementation does what the design says **and nothing more**, honestly:
|
|
35
|
+
|
|
36
|
+
- **Conformance.** Each Required Capability is delivered, and the service's generated
|
|
37
|
+
contract (OpenAPI/AsyncAPI/proto, captured from the running code) matches the prose
|
|
38
|
+
shapes — field names, types, status codes, error shapes.
|
|
39
|
+
- **Nothing more.** An undeclared endpoint, a field beyond the design, a behaviour the
|
|
40
|
+
slice was not asked for is scope creep — a finding even when it works. Scope that
|
|
41
|
+
exceeds the design is risk the review did not sign off on.
|
|
42
|
+
- **Honesty.** The implementation must satisfy its proof for the right reason, against the
|
|
43
|
+
real product. A return value hardcoded to the test's expected output, an input
|
|
44
|
+
special-cased to the fixture, a `if TEST_MODE`-style branch, a real unit of work mocked
|
|
45
|
+
out where the proof meant the real thing, or an error case the design names but the code
|
|
46
|
+
silently skips — each is a finding even though the suite is green. A weak implementation a
|
|
47
|
+
green suite passes is worse than none.
|
|
48
|
+
- **A fake needs a real test behind it.** When the diff (or its test) leans on a fixture,
|
|
49
|
+
stub, or fake file for work a real stage should do, some test must exercise the real
|
|
50
|
+
producer. A fixture nothing real ever generates — a hand-written thumbnail no pipeline
|
|
51
|
+
stage produces, a seeded record no code path writes — is a green light wired to nothing,
|
|
52
|
+
and a finding (`docs/principles/foundations/testing.md`).
|
|
53
|
+
- **Proven against the shipping build.** Where the slice contributes to a milestone's
|
|
54
|
+
front-door proof, the work it adds must live in the artifact the consumer actually
|
|
55
|
+
launches — the packaged app, the embedded worker — not only in a test target that runs
|
|
56
|
+
code the shipping build never includes.
|
|
57
|
+
|
|
58
|
+
You judge against the design, not against general taste — a correctness bug with no
|
|
59
|
+
design angle belongs to the blind reviewer, an unhandled edge to the tracer, a thin test
|
|
60
|
+
suite to the coverage auditor. Stay on conformance and honesty.
|
|
61
|
+
|
|
62
|
+
## The report
|
|
63
|
+
|
|
64
|
+
For each finding: a one-line title, the location (file and line), the specific Required
|
|
65
|
+
Capability or design shape it violates (quote the prose), and why it matters. Suggest a
|
|
66
|
+
nature (decision-needed / patch / defer / dismiss); the driver makes the final call and
|
|
67
|
+
dedupes across the four lenses. If the diff conforms and is honest, say so in one line.
|
|
68
|
+
Keep it to the findings.
|