harness-everything 0.43.1 → 0.45.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +17 -1
- package/ci/fixtures/doc-obligations/reconciliation-s2a/harness.js +19 -1
- package/ci/mechanism-2ax-doc-lifecycle.test.js +186 -2
- package/ci/mechanism-325-f2-doc-craft.test.js +2 -2
- package/ci/mechanism-325-f3-derived-views.test.js +5 -2
- package/ci/mechanism-pr-a-conflict-check.test.js +507 -0
- package/docs/doc-workflow-reconciliation.md +2 -1
- package/docs/own-repo-governance-e1-plan-2026-10-25-2139.md +2 -0
- package/docs/platform-capabilities.md +4 -0
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-check.js +84 -6
- package/harness-everything/scripts/doc-lifecycle.js +51 -6
- package/hooks/scripts/lib/doc-reconciliation-events.js +17 -0
- package/hooks/scripts/lib/doc-reconciliation-pilot.js +3 -0
- package/hooks/scripts/lib/doc-reconciliation-provider.js +80 -7
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-events.js +17 -0
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-pilot.js +3 -0
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-provider.js +80 -7
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-check.js +84 -6
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +51 -6
- package/sbom.cdx.json +7 -7
- package/scripts/doc-governance-guard.js +77 -21
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "harness-everything",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.45.0",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Miya Daniel"
|
|
16
16
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
3
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.45.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.45.0](https://github.com/dyphn1/Harness-everything/compare/v0.44.0...v0.45.0) (2026-10-27)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **doc-lifecycle:** produce conflict-check from a full-corpus assessment ([ec596e0](https://github.com/dyphn1/Harness-everything/commit/ec596e02de554f14778f7790551b6990a50dd0b6)), closes [#325](https://github.com/dyphn1/Harness-everything/issues/325)
|
|
10
|
+
|
|
11
|
+
## [0.44.0](https://github.com/dyphn1/Harness-everything/compare/v0.43.1...v0.44.0) (2026-10-27)
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **governance:** accept git-aware globs in the base-owned guard ([674a96d](https://github.com/dyphn1/Harness-everything/commit/674a96d7295555926ab3bb1b8ca0204cf59dbad5)), closes [#325](https://github.com/dyphn1/Harness-everything/issues/325)
|
|
16
|
+
|
|
5
17
|
## [0.43.1](https://github.com/dyphn1/Harness-everything/compare/v0.43.0...v0.43.1) (2026-10-27)
|
|
6
18
|
|
|
7
19
|
### Bug Fixes
|
|
@@ -10,9 +22,13 @@ All notable changes to this project are documented in this file.
|
|
|
10
22
|
|
|
11
23
|
## [Unreleased]
|
|
12
24
|
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* **doc-lifecycle:** add complete full-corpus conflict-check evidence with snapshot-bound scope and corpus identity.
|
|
28
|
+
|
|
13
29
|
### Fixed
|
|
14
30
|
|
|
15
|
-
- **doc-governance:** allow regular governed-content revisions while retaining review for structural, policy, trust-surface, fixture, and derived-output changes; run the guard in its own CI job.
|
|
31
|
+
- **doc-governance:** allow regular governed-content revisions and expand base-owned governed/derived globs against Git trees, while retaining review for membership, structural, policy, trust-surface, fixture, and derived-output changes; run the guard in its own CI job.
|
|
16
32
|
|
|
17
33
|
## [0.43.0](https://github.com/dyphn1/Harness-everything/compare/v0.42.2...v0.43.0) (2026-10-27)
|
|
18
34
|
|
|
@@ -400,7 +400,7 @@ function buildEvents(snapshot, repo, vec) {
|
|
|
400
400
|
originRef: `fake-origin:event:${ref}`
|
|
401
401
|
};
|
|
402
402
|
if (kind === 'execution') {
|
|
403
|
-
|
|
403
|
+
const event = {
|
|
404
404
|
...common,
|
|
405
405
|
kind: 'execution',
|
|
406
406
|
principalId: PRINCIPALS.executor,
|
|
@@ -415,6 +415,24 @@ function buildEvents(snapshot, repo, vec) {
|
|
|
415
415
|
resultRef: `result-${capability}`,
|
|
416
416
|
artifactDigests: clone(artifactDigests)
|
|
417
417
|
};
|
|
418
|
+
if (capability === 'conflict-check') {
|
|
419
|
+
const { documentCorpusPaths } = require(path.join(ROOT, 'harness-everything', 'scripts', 'doc-lifecycle.js'));
|
|
420
|
+
const configSource = snapshot.config.capture.sources.find(item => item.path === CONFIG_REL && item.presence === 'present');
|
|
421
|
+
const configBytes = Buffer.isBuffer(configSource?.bytes) ? configSource.bytes : Buffer.from(configSource?.bytes?.data || []);
|
|
422
|
+
const corpusPaths = documentCorpusPaths(snapshot.candidate.records, snapshot.config.resolution.config);
|
|
423
|
+
event.corpusBinding = {
|
|
424
|
+
complete: true,
|
|
425
|
+
paths: corpusPaths,
|
|
426
|
+
digests: Object.fromEntries(corpusPaths.map(relative => [relative,
|
|
427
|
+
snapshot.candidate.records.find(item => item.path === relative && item.presence === 'present')?.digest])),
|
|
428
|
+
config: { path: CONFIG_REL, digest: sha256(configBytes) },
|
|
429
|
+
baseCommit: snapshot.originContext.baseline.commit,
|
|
430
|
+
baselineDigest: snapshot.identity.baselineDigest,
|
|
431
|
+
candidateDigest: snapshot.identity.candidateDigest,
|
|
432
|
+
configIdentityDigest: snapshot.identity.configDigest,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
events[ref] = event;
|
|
418
436
|
} else {
|
|
419
437
|
events[ref] = {
|
|
420
438
|
...common,
|
|
@@ -1667,8 +1667,8 @@ checkCase('2ax. frozen Git-object guard with bootstrap and tamper controls', ()
|
|
|
1667
1667
|
return fixtureGit(root, ['rev-parse', 'HEAD']).toString('ascii').trim();
|
|
1668
1668
|
}
|
|
1669
1669
|
|
|
1670
|
-
function createFixture() {
|
|
1671
|
-
const root = workspace(
|
|
1670
|
+
function createFixture(name = 'governance-guard-git') {
|
|
1671
|
+
const root = workspace(name);
|
|
1672
1672
|
fs.mkdirSync(path.join(root, '.fixture-hooks'), { recursive: true });
|
|
1673
1673
|
fixtureGit(root, ['init', '--quiet']);
|
|
1674
1674
|
fixtureGit(root, ['config', 'user.name', 'Harness Guard Test']);
|
|
@@ -1820,6 +1820,190 @@ checkCase('2ax. frozen Git-object guard with bootstrap and tamper controls', ()
|
|
|
1820
1820
|
assert.deepStrictEqual(derivedEntry.base, configuredIdentity(fixture.base, 'docs/governed-index.md'));
|
|
1821
1821
|
assert.deepStrictEqual(derivedEntry.candidate, configuredIdentity(derivedCandidate, 'docs/governed-index.md'));
|
|
1822
1822
|
|
|
1823
|
+
const globPatterns = [
|
|
1824
|
+
'docs/adr/*.md',
|
|
1825
|
+
'empty/*.md',
|
|
1826
|
+
'corpus/one*.md', // portability-lint: allow glob pattern string, never created as a file
|
|
1827
|
+
'corpus/*.md',
|
|
1828
|
+
'deep/**/*.md',
|
|
1829
|
+
'repeat/**/repeat/*.md',
|
|
1830
|
+
'literal.v1/**/*.md'
|
|
1831
|
+
];
|
|
1832
|
+
const corpusPaths = [
|
|
1833
|
+
'docs/adr/one.md', 'docs/adr/two.md', 'generated/view.md', 'generated/nested/view.md',
|
|
1834
|
+
'corpus/one.md', 'corpus/many-a.md', 'corpus/many-b.md',
|
|
1835
|
+
'deep/zero.md', 'deep/a/b.md',
|
|
1836
|
+
'repeat/repeat/first.md', 'repeat/a/repeat/second.md',
|
|
1837
|
+
'literal.v1/top.md', 'literal.v1/sub/nested.md', 'literalXv1/no.md'
|
|
1838
|
+
];
|
|
1839
|
+
const globFixture = createFixture('governance-guard-glob-git');
|
|
1840
|
+
resetFixture(globFixture);
|
|
1841
|
+
const globPolicy = {
|
|
1842
|
+
governed: globPatterns.map(pattern => ({ path: pattern, role: 'reference' })),
|
|
1843
|
+
derived: [{ path: 'generated/**/*.md', generator: 'build' }]
|
|
1844
|
+
};
|
|
1845
|
+
writeRelative(globFixture.root, '.agents/harness-everything/project-docs.json', `${JSON.stringify(globPolicy, null, 2)}\n`);
|
|
1846
|
+
for (const relativePath of corpusPaths) writeRelative(globFixture.root, relativePath, `baseline ${relativePath}\n`);
|
|
1847
|
+
globFixture.base = commitFixture(globFixture.root, 'glob policy baseline');
|
|
1848
|
+
|
|
1849
|
+
const globAccepted = runFrozenGuard(globFixture, globFixture.base, globFixture.base);
|
|
1850
|
+
assert.strictEqual(globAccepted.report.verdict, 'PASS', globAccepted.report.errors[0]?.message);
|
|
1851
|
+
const globSummaries = globAccepted.report.policyPreservation.configuredPaths.patterns;
|
|
1852
|
+
assert.ok(globSummaries, 'glob records report their base and candidate member counts');
|
|
1853
|
+
assert.strictEqual(globAccepted.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/one.md').state, 'pass', 'unchanged glob members pass');
|
|
1854
|
+
assert.deepStrictEqual(runFrozenGuard(globFixture, globFixture.base, globFixture.base), globAccepted, 'glob reports are deterministic for identical Git inputs');
|
|
1855
|
+
const lifecycleSource = fs.readFileSync(modulePath, 'utf8');
|
|
1856
|
+
const lifecycleMatcherSource = lifecycleSource.match(/function globToRegExp\(glob\) \{[\s\S]*?\n\}/);
|
|
1857
|
+
assert.ok(lifecycleMatcherSource, 'doc-lifecycle globToRegExp remains available for the consistency check');
|
|
1858
|
+
const lifecycleGlobToRegExp = new Function(`${lifecycleMatcherSource[0]}\nreturn globToRegExp;`)();
|
|
1859
|
+
for (const pattern of globPatterns) {
|
|
1860
|
+
const expected = corpusPaths.filter(relativePath => lifecycleGlobToRegExp(pattern).test(relativePath)).length;
|
|
1861
|
+
const summary = globSummaries.find(item => item.kind === 'governed' && item.path === pattern);
|
|
1862
|
+
assert.ok(summary, `glob summary exists for ${pattern}`);
|
|
1863
|
+
assert.strictEqual(summary.baseMembers, expected, `guard matcher agrees with doc-lifecycle for ${pattern}`);
|
|
1864
|
+
assert.strictEqual(summary.candidateMembers, expected, `candidate matcher agrees with doc-lifecycle for ${pattern}`);
|
|
1865
|
+
const expectedPaths = corpusPaths.filter(relativePath => lifecycleGlobToRegExp(pattern).test(relativePath)).sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
|
|
1866
|
+
const actualPaths = globAccepted.report.policyPreservation.configuredPaths.paths
|
|
1867
|
+
.filter(item => item.kind === 'governed' && item.pattern === pattern).map(item => item.path);
|
|
1868
|
+
assert.deepStrictEqual(actualPaths, expectedPaths, `guard matcher returns the same members as doc-lifecycle for ${pattern}`);
|
|
1869
|
+
}
|
|
1870
|
+
assert.strictEqual(globSummaries.find(item => item.path === 'empty/*.md').baseMembers, 0, 'an empty glob is legal and reports zero members');
|
|
1871
|
+
|
|
1872
|
+
resetFixture(globFixture);
|
|
1873
|
+
writeRelative(globFixture.root, 'docs/adr/one.md', 'revised governed glob member\n');
|
|
1874
|
+
const globRevisionCandidate = commitFixture(globFixture.root, 'revise glob member');
|
|
1875
|
+
const globRevision = runFrozenGuard(globFixture, globFixture.base, globRevisionCandidate);
|
|
1876
|
+
assert.strictEqual(globRevision.status, 0, 'a regular governed glob member revision passes');
|
|
1877
|
+
const globRevisionEntry = globRevision.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/one.md');
|
|
1878
|
+
assert.strictEqual(globRevisionEntry.state, 'revised');
|
|
1879
|
+
|
|
1880
|
+
resetFixture(globFixture);
|
|
1881
|
+
writeRelative(globFixture.root, 'docs/adr/new.md', 'new governed glob member\n');
|
|
1882
|
+
const globAdditionCandidate = commitFixture(globFixture.root, 'add glob member');
|
|
1883
|
+
const globAddition = runFrozenGuard(globFixture, globFixture.base, globAdditionCandidate);
|
|
1884
|
+
assert.strictEqual(globAddition.status, 2, 'a new glob member requires review');
|
|
1885
|
+
assert.strictEqual(globAddition.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/new.md').state, 'review_required');
|
|
1886
|
+
|
|
1887
|
+
resetFixture(globFixture);
|
|
1888
|
+
fixtureGit(globFixture.root, ['rm', '--quiet', '--', 'docs/adr/two.md']);
|
|
1889
|
+
const globDeletionCandidate = commitFixture(globFixture.root, 'delete glob member');
|
|
1890
|
+
const globDeletion = runFrozenGuard(globFixture, globFixture.base, globDeletionCandidate);
|
|
1891
|
+
assert.strictEqual(globDeletion.status, 2, 'a deleted glob member requires review');
|
|
1892
|
+
assert.strictEqual(globDeletion.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/two.md').candidate, null);
|
|
1893
|
+
|
|
1894
|
+
resetFixture(globFixture);
|
|
1895
|
+
fixtureGit(globFixture.root, ['mv', '--', 'docs/adr/one.md', 'docs/adr/renamed.md']);
|
|
1896
|
+
const globRenameCandidate = commitFixture(globFixture.root, 'rename glob member');
|
|
1897
|
+
const globRename = runFrozenGuard(globFixture, globFixture.base, globRenameCandidate);
|
|
1898
|
+
const globRenamePaths = globRename.report.policyPreservation.configuredPaths.paths;
|
|
1899
|
+
assert.strictEqual(globRename.status, 2, 'a rename within a glob requires review');
|
|
1900
|
+
assert.strictEqual(globRenamePaths.find(item => item.path === 'docs/adr/one.md').candidate, null, 'rename retains the old endpoint');
|
|
1901
|
+
assert.strictEqual(globRenamePaths.find(item => item.path === 'docs/adr/renamed.md').base, null, 'rename retains the new endpoint');
|
|
1902
|
+
|
|
1903
|
+
for (const mutation of ['mode', 'symlink', 'gitlink']) {
|
|
1904
|
+
resetFixture(globFixture);
|
|
1905
|
+
if (mutation === 'mode') {
|
|
1906
|
+
const candidate = commitFixture(globFixture.root, 'change glob member mode', 'docs/adr/one.md');
|
|
1907
|
+
const result = runFrozenGuard(globFixture, globFixture.base, candidate);
|
|
1908
|
+
assert.strictEqual(result.status, 2, 'a glob member mode change requires review');
|
|
1909
|
+
assert.strictEqual(result.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/one.md').state, 'review_required');
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
fs.rmSync(path.join(globFixture.root, 'docs', 'adr', 'one.md'));
|
|
1913
|
+
if (mutation === 'symlink') {
|
|
1914
|
+
const oid = fixtureGit(globFixture.root, ['hash-object', '-w', '--stdin'], Buffer.from('../outside-target.md\n')).toString('ascii').trim();
|
|
1915
|
+
fixtureGit(globFixture.root, ['add', '-A']);
|
|
1916
|
+
fixtureGit(globFixture.root, ['update-index', '--add', '--cacheinfo', `120000,${oid},docs/adr/one.md`]);
|
|
1917
|
+
} else {
|
|
1918
|
+
fixtureGit(globFixture.root, ['add', '-A']);
|
|
1919
|
+
fixtureGit(globFixture.root, ['update-index', '--add', '--cacheinfo', `160000,${globFixture.base},docs/adr/one.md`]);
|
|
1920
|
+
}
|
|
1921
|
+
fixtureGit(globFixture.root, ['commit', '--quiet', '-m', `replace glob member with ${mutation}`]);
|
|
1922
|
+
const candidate = fixtureGit(globFixture.root, ['rev-parse', 'HEAD']).toString('ascii').trim();
|
|
1923
|
+
const result = runFrozenGuard(globFixture, globFixture.base, candidate);
|
|
1924
|
+
assert.strictEqual(result.status, 2, `a glob member ${mutation} requires review`);
|
|
1925
|
+
assert.strictEqual(result.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/one.md').state, 'review_required');
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
resetFixture(globFixture);
|
|
1929
|
+
writeRelative(globFixture.root, 'generated/view.md', 'changed derived glob output\n');
|
|
1930
|
+
const derivedGlobCandidate = commitFixture(globFixture.root, 'change derived glob member');
|
|
1931
|
+
const derivedGlob = runFrozenGuard(globFixture, globFixture.base, derivedGlobCandidate);
|
|
1932
|
+
assert.strictEqual(derivedGlob.status, 2, 'a derived glob member content change requires review');
|
|
1933
|
+
assert.strictEqual(derivedGlob.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'generated/view.md').state, 'review_required');
|
|
1934
|
+
|
|
1935
|
+
if (process.platform !== 'win32') {
|
|
1936
|
+
resetFixture(globFixture);
|
|
1937
|
+
const unusualPath = 'docs/adr/new\nfile.md';
|
|
1938
|
+
const oid = fixtureGit(globFixture.root, ['hash-object', '-w', '--stdin'], Buffer.from('unusual glob member\n')).toString('ascii').trim();
|
|
1939
|
+
fixtureGit(globFixture.root, ['update-index', '--add', '--cacheinfo', `100644,${oid},${unusualPath}`]);
|
|
1940
|
+
fixtureGit(globFixture.root, ['commit', '--quiet', '-m', 'add unusual glob member']);
|
|
1941
|
+
const unusualCandidate = fixtureGit(globFixture.root, ['rev-parse', 'HEAD']).toString('ascii').trim();
|
|
1942
|
+
const unusual = runFrozenGuard(globFixture, globFixture.base, unusualCandidate);
|
|
1943
|
+
assert.strictEqual(unusual.status, 2, 'a glob matches paths containing unusual LF bytes');
|
|
1944
|
+
assert.strictEqual(unusual.report.policyPreservation.configuredPaths.paths.find(item => item.path === unusualPath).state, 'review_required');
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
if (process.platform !== 'win32') {
|
|
1948
|
+
// Known divergence: doc-lifecycle.js still excludes line terminators in glob segments.
|
|
1949
|
+
const newlineGlobFixture = createFixture('governance-guard-newline-glob-git');
|
|
1950
|
+
resetFixture(newlineGlobFixture);
|
|
1951
|
+
writeRelative(newlineGlobFixture.root, '.agents/harness-everything/project-docs.json', `${JSON.stringify({
|
|
1952
|
+
governed: [{ path: 'docs/**/*.md', role: 'reference' }],
|
|
1953
|
+
derived: []
|
|
1954
|
+
}, null, 2)}\n`);
|
|
1955
|
+
writeRelative(newlineGlobFixture.root, 'docs/anchor.md', 'baseline direct child\n');
|
|
1956
|
+
writeRelative(newlineGlobFixture.root, 'docs/a\nb/file.md', 'baseline newline directory member\n');
|
|
1957
|
+
writeRelative(newlineGlobFixture.root, 'docs/a/b/file.md', 'baseline regular directory member\n');
|
|
1958
|
+
newlineGlobFixture.base = commitFixture(newlineGlobFixture.root, 'newline glob policy baseline');
|
|
1959
|
+
|
|
1960
|
+
fixtureGit(newlineGlobFixture.root, ['rm', '--quiet', '--', 'docs/a\nb/file.md']);
|
|
1961
|
+
const newlineDeletionCandidate = commitFixture(newlineGlobFixture.root, 'delete newline directory member');
|
|
1962
|
+
const newlineDeletion = runFrozenGuard(newlineGlobFixture, newlineGlobFixture.base, newlineDeletionCandidate);
|
|
1963
|
+
|
|
1964
|
+
resetFixture(newlineGlobFixture);
|
|
1965
|
+
writeRelative(newlineGlobFixture.root, 'docs/a\nb/new.md', 'new newline directory member\n');
|
|
1966
|
+
const newlineAdditionCandidate = commitFixture(newlineGlobFixture.root, 'add newline directory member');
|
|
1967
|
+
const newlineAddition = runFrozenGuard(newlineGlobFixture, newlineGlobFixture.base, newlineAdditionCandidate);
|
|
1968
|
+
|
|
1969
|
+
resetFixture(newlineGlobFixture);
|
|
1970
|
+
fixtureGit(newlineGlobFixture.root, ['rm', '--quiet', '--', 'docs/a/b/file.md']);
|
|
1971
|
+
const regularDeletionCandidate = commitFixture(newlineGlobFixture.root, 'delete regular directory member');
|
|
1972
|
+
const regularDeletion = runFrozenGuard(newlineGlobFixture, newlineGlobFixture.base, regularDeletionCandidate);
|
|
1973
|
+
|
|
1974
|
+
assert.deepStrictEqual({
|
|
1975
|
+
newlineDirectoryDeletion: newlineDeletion.status,
|
|
1976
|
+
newlineDirectoryAddition: newlineAddition.status,
|
|
1977
|
+
regularDirectoryDeletion: regularDeletion.status
|
|
1978
|
+
}, {
|
|
1979
|
+
newlineDirectoryDeletion: 2,
|
|
1980
|
+
newlineDirectoryAddition: 2,
|
|
1981
|
+
regularDirectoryDeletion: 2
|
|
1982
|
+
}, 'newline-directory deletions and additions require review; regular-directory deletion is the positive control');
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
resetFixture(globFixture);
|
|
1986
|
+
const narrowedPolicy = JSON.parse(fs.readFileSync(path.join(globFixture.root, '.agents/harness-everything/project-docs.json'), 'utf8'));
|
|
1987
|
+
narrowedPolicy.governed[0].path = 'docs/adr/one.md';
|
|
1988
|
+
writeRelative(globFixture.root, '.agents/harness-everything/project-docs.json', `${JSON.stringify(narrowedPolicy, null, 2)}\n`);
|
|
1989
|
+
fixtureGit(globFixture.root, ['rm', '--quiet', '--', 'docs/adr/two.md']);
|
|
1990
|
+
const narrowedCandidate = commitFixture(globFixture.root, 'narrow governed glob');
|
|
1991
|
+
const narrowed = runFrozenGuard(globFixture, globFixture.base, narrowedCandidate);
|
|
1992
|
+
assert.strictEqual(narrowed.status, 2, 'candidate policy cannot narrow base glob coverage');
|
|
1993
|
+
assert.strictEqual(narrowed.report.policyPreservation.governed.state, 'review_required', 'candidate glob changes remain policy review');
|
|
1994
|
+
assert.strictEqual(narrowed.report.policyPreservation.configuredPaths.paths.find(item => item.path === 'docs/adr/two.md').candidate, null);
|
|
1995
|
+
|
|
1996
|
+
for (const unsupported of ['docs/adr/*.md?', 'docs/adr/[a].md', 'docs/adr/{a}.md', '/docs/adr/*.md', 'docs/../*.md']) {
|
|
1997
|
+
resetFixture(globFixture);
|
|
1998
|
+
const invalidPolicy = JSON.parse(fs.readFileSync(path.join(globFixture.root, '.agents/harness-everything/project-docs.json'), 'utf8'));
|
|
1999
|
+
invalidPolicy.governed.push({ path: unsupported, role: 'reference' });
|
|
2000
|
+
writeRelative(globFixture.root, '.agents/harness-everything/project-docs.json', `${JSON.stringify(invalidPolicy, null, 2)}\n`);
|
|
2001
|
+
const invalidCandidate = commitFixture(globFixture.root, `unsupported pattern ${unsupported}`);
|
|
2002
|
+
const invalid = runFrozenGuard(globFixture, globFixture.base, invalidCandidate);
|
|
2003
|
+
assert.strictEqual(invalid.status, 1, `unsupported glob syntax fails closed: ${unsupported}`);
|
|
2004
|
+
assert.strictEqual(invalid.report.verdict, 'FAIL');
|
|
2005
|
+
}
|
|
2006
|
+
|
|
1823
2007
|
for (const mutation of ['rename', 'mode', 'symlink']) {
|
|
1824
2008
|
resetFixture(fixture);
|
|
1825
2009
|
const relativePath = 'docs/architecture.md';
|
|
@@ -502,8 +502,8 @@ function mp2Apply() {
|
|
|
502
502
|
const dir = scriptCopy('f2 doc craft mp2 ');
|
|
503
503
|
const file = path.join(dir, MODULE);
|
|
504
504
|
const original = fs.readFileSync(file, 'utf8');
|
|
505
|
-
const emitMatches = [...original.matchAll(/return emit\(result
|
|
506
|
-
if (emitMatches.length !== 1) throw new ProbeApplyError(`could not uniquely locate the
|
|
505
|
+
const emitMatches = [...original.matchAll(/return emit\(result, lintExit === 1 \|\| broken\.length \? 1 : 0\);/g)];
|
|
506
|
+
if (emitMatches.length !== 1) throw new ProbeApplyError(`could not uniquely locate the plain-check exit mapping (${emitMatches.length} candidates); update this probe for the green shape`);
|
|
507
507
|
const injected = `if (result.reminders && result.reminders.length) return emit(result, 1);\n ${emitMatches[0][0]}`;
|
|
508
508
|
fs.writeFileSync(file, original.replace(emitMatches[0][0], injected));
|
|
509
509
|
return dir;
|
|
@@ -356,8 +356,11 @@ function caseD13(leg) {
|
|
|
356
356
|
const candidate = path.join(dir, 'candidate.md');
|
|
357
357
|
fs.writeFileSync(candidate, Buffer.from(TARGET_BYTES.toString('utf8').replace(/\n/g, '\r\n'), 'utf8'));
|
|
358
358
|
const result = runCompare(leg.dir, { workspace: ws, config: CONFIG, view: 'docs/index.md', candidate, status: 'ok' });
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
let comparison;
|
|
360
|
+
try { comparison = JSON.parse(result.stdout); } catch { comparison = null; }
|
|
361
|
+
const detail = `compare-derived JSON: ${JSON.stringify(comparison)}; stdout: ${truncate(result.stdout)}; stderr: ${truncate(result.stderr)}`;
|
|
362
|
+
same(result.status, 1, `exit; ${detail}`);
|
|
363
|
+
if (!comparison || !JSON.stringify(comparison).includes('stale')) throw new Error(`CRLF candidate accepted; ${detail}`);
|
|
361
364
|
}
|
|
362
365
|
assertPerLeg('case-d13:crlf-candidate-not-normalized', leg => caseD13(leg));
|
|
363
366
|
|