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
26 changes: 22 additions & 4 deletions scripts/fetch-sitrep.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import path from 'node:path';
// network: reads public GitHub repository metadata and repo-owned reports; optional token raises rate limits
// storage: writes generated and last-known-good SITREP JSON only
// authority: skill-lib owns the reporting contract and deterministic projection; each repository owns its report claims; this site owns presentation only
// failure: never reconstructs missing reports; never publishes raw command errors or credentials; missing or HEAD-different sources remain visible and a last-known-good projection may be used only with fallback=true
// failure: never reconstructs missing reports; never publishes raw command errors or credentials; missing or source-different reports remain visible and a last-known-good projection may be used only with fallback=true
// === END BOUNDARIES ===
// Usage: run `npm run refresh:sitrep`; the output is presentation data, not a new source of repository canon.

Expand All @@ -28,7 +28,7 @@ const localReportPath = 'docs/work-graphs/repository-plan-report.json';
// reinterpret repo reports when skill-lib main changes.
const controlPlane = {
repository: `${org}/skill-lib`,
commit: '6ef2e4c123225f9db20e5230e5894c9c86b42ee6',
commit: 'c14ee9d500579a4b5d6821f62c9d82ca96e73608',
skill: 'interdependent-work-graph',
reportSchemaVersion: '1.0.0',
reportSchemaPath: 'interdependent-work-graph/repository-plan-report.schema.json',
Expand All @@ -37,11 +37,20 @@ const controlPlane = {
portfolioScriptBlob: '97b8b546b4151486164c8a4b730c24a8c895b25b'
};

// Explicit portfolio membership: this is the evidence-bounded core graph, not
// organization-wide repository discovery. Repositories outside this set remain
// outside the current projection until a deliberate membership decision adds them.
const projectDefinitions = [
{ repository: `${org}/skill-lib`, name: 'skill-lib', label: 'Skill Library', slug: 'skill-lib' },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve expanded membership when using the fallback snapshot

When the refresh falls back—for example with OFFLINE=1 or during a control-plane API outage—fallbackData spreads the committed last-known-good snapshot without reconciling it with this expanded list. That snapshot still contains only the original five projects, so skill-lib, pcea, ptcna, epac, zfae, and stack disappear entirely instead of remaining visible with unavailable reports; an offline run at this commit reproduces a five-project result with the old control-plane commit. Update the bootstrap snapshot or merge projectDefinitions into fallback data so the explicit current portfolio remains represented.

Useful? React with 👍 / 👎.

{ repository: `${org}/metapat`, name: 'metapat', label: 'METAPAT', slug: 'metapat' },
{ repository: `${org}/ucns`, name: 'ucns', label: 'UCNS', slug: 'ucns' },
{ repository: `${org}/edcm`, name: 'edcm', label: 'EDCM', slug: 'edcm' },
{ repository: `${org}/pcea`, name: 'pcea', label: 'PCEA', slug: 'pcea' },
{ repository: `${org}/ptcna`, name: 'ptcna', label: 'PTCNA', slug: 'ptcna' },
{ repository: `${org}/epac`, name: 'epac', label: 'EPAC', slug: 'epac' },
{ repository: `${org}/zfae`, name: 'zfae', label: 'ZFAE', slug: 'zfae' },
{ repository: `${org}/a0`, name: 'a0', label: 'a0', slug: 'a0' },
{ repository: `${org}/stack`, name: 'stack', label: 'Stack', slug: 'stack' },
Comment on lines +48 to +53

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep unauthenticated refreshes within GitHub's API limit

When npm run refresh:data runs without GITHUB_TOKEN (which these collectors document as optional), expanding this list to 11 repositories raises this script to 56 REST requests: 2 control-plane lookups, 10 remote reports, and 4 telemetry requests per project. The preceding canon and repository refreshes already make about 24 requests for the currently tracked repositories, exceeding GitHub's 60-request/hour unauthenticated limit; later projects consequently receive null telemetry, and that incomplete result is still persisted as a non-fallback last-known-good snapshot. Reuse the heads/metadata already fetched by refresh:github, consolidate these calls, or explicitly require authentication before expanding the portfolio.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the hardcoded five-project SITREP copy

Expanding the portfolio to 11 repositories leaves several user-facing strings describing the old scope: src/sitrep/index.njk still names only METAPAT, UCNS, EDCM, a0, and the website in its page description and labels the cards as “Five distinct authority surfaces,” while src/assets/js/sitrep.js describes external graph nodes as outside “the five participating SITREP reports.” Consequently the rendered page and its metadata contradict the generated 11-project count; update these strings or derive the count and scope dynamically.

Useful? React with 👍 / 👎.

{ repository: websiteRepository, name: 'The-Interdependency.github.io', label: 'Website', slug: 'website', localReport: true }
];

Expand Down Expand Up @@ -143,6 +152,8 @@ function collectTelemetry(definition) {
return {
branch,
head: commit.sha || null,
parents: (commit.parents || []).map(parent => parent.sha).filter(Boolean),
changedFiles: (commit.files || []).map(file => file.filename).filter(Boolean),
headDate: commit.commit?.committer?.date || commit.commit?.author?.date || null,
pushedAt: repo.pushed_at || null,
updatedAt: repo.updated_at || null,
Expand All @@ -162,8 +173,15 @@ function collectTelemetry(definition) {
function projectView(definition, reportRecord, telemetry, portfolioView) {
const sourceCommit = reportRecord?.report?.source?.commit || null;
const head = telemetry?.head || null;
const reportOnlyRefresh = Boolean(
sourceCommit &&
head &&
(telemetry?.parents || []).includes(sourceCommit) &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match the report source against the first parent

For a merge commit, accepting any parent can mark an outdated report current: if the declared source is the second parent while the first parent contains newer substantive work, the commit's changed-file list can still contain only the report relative to the first parent. The intended report-refresh and report-only merge cases use the declared source as the first parent, so this predicate should require that parent specifically rather than using includes.

Useful? React with 👍 / 👎.

(telemetry?.changedFiles || []).length === 1 &&
telemetry.changedFiles[0] === localReportPath
);
let reportFreshness = 'unknown';
if (sourceCommit && head) reportFreshness = sourceCommit === head ? 'current' : 'HEAD differs';
if (sourceCommit && head) reportFreshness = sourceCommit === head || reportOnlyRefresh ? 'current' : 'HEAD differs';
else if (!reportRecord) reportFreshness = 'missing';
return {
...definition,
Expand Down Expand Up @@ -290,4 +308,4 @@ try {
console.log(`sitrep fallback: ${data.fallbackReason}`);
} finally {
if (workDir) await rm(workDir, { recursive: true, force: true });
}
}
29 changes: 27 additions & 2 deletions tests/sitrep.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@
assert.match(source, /missingReports/);
});

test('SITREP current portfolio explicitly covers the evidence-bounded core graph', async () => {
const source = await readFile('scripts/fetch-sitrep.mjs', 'utf8');
for (const repository of [
'skill-lib',
'metapat',
'ucns',
'edcm',
'pcea',
'ptcna',
'epac',
'zfae',
'a0',
'stack',
'The-Interdependency.github.io'
]) {
assert.match(source, new RegExp(repository.replaceAll('.', '\\.')));
}
assert.match(source, /Explicit portfolio membership/);
assert.doesNotMatch(source, /org-wide auto-discovery/i);
});

test('SITREP failure publication classifies errors instead of echoing command details', async () => {
const source = await readFile('scripts/fetch-sitrep.mjs', 'utf8');
assert.match(source, /function publicFailureReason/);
Expand All @@ -21,9 +42,13 @@
assert.doesNotMatch(source, /reason:\s*error\.message/);
});

test('HEAD mismatch is exposed as difference without inferring substantive staleness', async () => {
test('report-only coordination commits do not make a self-report stale by construction', async () => {
const source = await readFile('scripts/fetch-sitrep.mjs', 'utf8');
assert.match(source, /sourceCommit === head \? 'current' : 'HEAD differs'/);
assert.match(source, /reportOnlyRefresh/);
assert.match(source, /parents/);
assert.match(source, /changedFiles/);
assert.match(source, /telemetry\.changedFiles\[0\] === localReportPath/);
assert.match(source, /sourceCommit === head \|\| reportOnlyRefresh \? 'current' : 'HEAD differs'/);
assert.doesNotMatch(source, /sourceCommit === head \? 'current' : 'stale'/);
});

Expand Down
Loading