Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ops/local-work-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,15 @@ async function verifiedPackage() {
}

async function report() {
// Assert scope AND run the acceptance argv before printing anything. Without
// this, an unattended local drive tick could exit "REPORT ... DoD: X" while
// the DoD is unmet: verifiedPackage only validates the contract's integrity,
// it does not execute the checks that prove the actual state satisfies it.
// The scope refusal is already covered by verifiedPackage -> checkScope;
// runChecks makes the DoD assertion executable rather than decorative (#271).
const pkg = await verifiedPackage();
runChecks(pkg);
await verifiedPackage();
// The package pins the HEAD it was selected against. Reporting a diff from a
// different commit would describe work this tick did not do.
const head = git('rev-parse', 'HEAD');
Expand All @@ -233,6 +241,7 @@ async function report() {
console.log(`REPORT ${pkg.title}`);
console.log(stat || ' (no working-tree changes)');
for (const item of pkg.definitionOfDone) console.log(` DoD: ${item}`);
console.log(`PACKAGE_VERIFIED: ${pkg.verificationCommands.length} check(s)`);
}

try {
Expand Down
28 changes: 27 additions & 1 deletion ops/local-work-review.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ for (const kind of ['unstaged', 'staged', 'untracked']) {
pass(f.run('select'));
const path = kind === 'untracked' ? 'src/new.txt' : 'src/value.txt';
f.put(path, 'fixed');
if (kind === 'untracked') f.put('src/value.txt', 'fixed'); // meet DoD alongside the untracked file (#271: report runs acceptance)
if (kind === 'staged') f.git('add', path);
pass(f.run('scope'));
const report = f.run('report');
Expand Down Expand Up @@ -43,14 +44,39 @@ test('selection skips untracked scope and accepts the next committed scope', t =
assert.match(result.stdout, /SELECTED Fix value/);
});

test('report refuses when the acceptance check fails against the working tree (#271)', t => {
const f = fixture(t);
pass(f.run('select'));
// Deliberately leave src/value.txt as its fixture-baseline "broken" — the DoD
// is unmet. Before #271 the report path only validated the acceptance
// contract's integrity and printed DoD strings verbatim, so it exited 0 with
// PACKAGE_VERIFIED regardless. After #271 report runs the argv and refuses.
const result = f.run('report');
fail(result, /AssertionError|Expected|broken/);
assert.doesNotMatch(result.stdout, /PACKAGE_VERIFIED/);
});

test('report prints PACKAGE_VERIFIED only after the acceptance check passes (#271)', t => {
const f = fixture(t);
pass(f.run('select'));
f.put('src/value.txt', 'fixed');
const result = f.run('report');
pass(result);
assert.match(result.stdout, /REPORT Fix value/);
assert.match(result.stdout, /PACKAGE_VERIFIED: 1 check/);
});

for (const path of ['outside.txt', 'src/value.txt']) {
test(`reporting after SDK suite effects enforces scope for ${path}`, t => {
const f = fixture(t);
pass(f.run('select'));
f.put('src/value.txt', 'fixed');
pass(f.run('verify'));
// Model a Git-visible effect produced by the SDK suite after package checks.
f.put(path, 'suite effect');
// For the in-scope case the "suite effect" must preserve the DoD-required
// state (writing "broken" back would clobber value.txt and correctly fail
// report's re-executed acceptance check post-#271); "fixed" preserves it.
f.put(path, path === 'src/value.txt' ? 'fixed' : 'suite effect');
let previous = 'verify';
let result;
while (previous !== 'report') {
Expand Down
Loading