Skip to content

[WIP] Add machine-readable PIR status sidecar for cross-cycle inheritance#2036

Merged
pethers merged 7 commits intomainfrom
copilot/add-pir-status-json-sidecar
Apr 27, 2026
Merged

[WIP] Add machine-readable PIR status sidecar for cross-cycle inheritance#2036
pethers merged 7 commits intomainfrom
copilot/add-pir-status-json-sidecar

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 27, 2026

Adds a machine-readable pir-status.json sidecar contract to support cross-cycle PIR inheritance, with a JSON Schema, roll-forward CLI, analysis-gate enforcement, and comprehensive Vitest coverage.

Changes Made

  • Added schemas/pir-status.schema.json using JSON Schema 2020-12, including conditional answer_summary validation and minLength: 1.
  • Added scripts/roll-forward-pirs.ts to roll forward PIRs across analysis cycles, degrade confidence for open PIRs, preserve non-open PIR inheritance history, validate source sidecars strictly, normalize inherited_from paths with path.relative(), and validate --max-lookback.
  • Updated .github/prompts/05-analysis-gate.md with PIR status sidecar enforcement as Check 9, including subfolder === cycle, PIR field validation, confidence/status validation, and conditional answer_summary checks.
  • Updated analysis/methodologies/ai-driven-analysis-guide.md to document pir-status.json creation, roll-forward behavior, and status preservation semantics.
  • Added tests/pir-status-contract.test.ts with direct-import unit and contract tests covering schema, validation, roll-forward behavior, CLI argument handling, gate references, and regression cases.

Testing

  • Targeted PIR contract tests pass: 81/81.
  • Full Vitest coverage passes: 2242/2242 tests.
  • Statement coverage: 84.77% (above the 80% threshold).
  • ESLint passes for changed TypeScript files.
  • Schema JSON validation passes.

@github-actions github-actions Bot added the size-xs Extra small change (< 10 lines) label Apr 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🏷️ Automatic Labeling Summary

This PR has been automatically labeled based on the files changed and PR metadata.

Applied Labels: size-xs

Label Categories

  • 🗳️ Content: news, dashboard, visualization, intelligence
  • 💻 Technology: html-css, javascript, workflow, security
  • 📊 Data: cia-data, riksdag-data, data-pipeline, schema
  • 🌍 I18n: i18n, translation, rtl
  • 🔒 ISMS: isms, iso-27001, nist-csf, cis-controls
  • 🏗️ Infrastructure: ci-cd, deployment, performance, monitoring
  • 🔄 Quality: testing, accessibility, documentation, refactor
  • 🤖 AI: agent, skill, agentic-workflow

For more information, see .github/labeler.yml.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@github-actions github-actions Bot added documentation Documentation updates schema Data schema changes testing Test coverage refactor Code refactoring size-xl Extra large change (> 1000 lines) labels Apr 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@pethers pethers marked this pull request as ready for review April 27, 2026 08:23
Copilot AI review requested due to automatic review settings April 27, 2026 08:23
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a machine-readable PIR status sidecar (pir-status.json) to support cross-cycle PIR inheritance, with a roll-forward script and CI analysis-gate enforcement.

Changes:

  • Introduces schemas/pir-status.schema.json (JSON Schema 2020-12) defining the PIR status sidecar contract.
  • Adds scripts/roll-forward-pirs.ts to propagate PIRs across analysis cycles with confidence degradation for open items.
  • Updates analysis gate + methodology docs, and adds Vitest contract/integration tests covering schema, gate references, and roll-forward behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
tests/pir-status-contract.test.ts Contract + integration tests for schema validity, gate references, and roll-forward CLI behavior.
scripts/roll-forward-pirs.ts New CLI tool to roll forward PIR sidecars between cycles and degrade confidence for open PIRs.
schemas/pir-status.schema.json New schema defining required fields and PIR entry structure for pir-status.json.
analysis/methodologies/ai-driven-analysis-guide.md Documents required pir-status.json generation and roll-forward usage.
.github/prompts/05-analysis-gate.md Adds Check 10 to require and structurally validate pir-status.json in the analysis output directory.

- Add one more named actor (MP, minister, official) to every stakeholder and SWOT entry.
- Add one more dok_id or vote-record citation to every evidence column that has < 2 citations.
- **Tag every key finding to a PIR/EEI** from the catalog in `political-style-guide.md`.
- **Write `pir-status.json`** — every cycle must produce `$ANALYSIS_DIR/pir-status.json` conforming to `schemas/pir-status.schema.json` v1.0 (required fields: `schema_version`, `cycle`, `date`, `subfolder`, `pirs`, `generated_at`). Extract PIRs from `intelligence-assessment.md` and set each status to `open`. This file is the machine-readable PIR sidecar used for automated roll-forward (see `scripts/roll-forward-pirs.ts`) and CI gate enforcement (Check 10 in `05-analysis-gate.md`).
Comment thread .github/prompts/05-analysis-gate.md Outdated
except Exception:
sys.exit(1)
" 2>/dev/null || { echo "❌ pir-status.json: 'pirs' field must be a JSON array"; FAIL=1; }
# each open PIR must have pir_id, statement, status, confidence
Comment thread scripts/roll-forward-pirs.ts Outdated
Comment on lines +183 to +184
// Non-open PIRs are carried forward unchanged for history.
return { ...p, inherits_from: [p.pir_id] };
Comment thread scripts/roll-forward-pirs.ts Outdated
Comment on lines +176 to +177
return idx < CONFIDENCE_ORDER.length - 1
? (CONFIDENCE_ORDER[idx + 1] as Confidence)
Comment thread scripts/roll-forward-pirs.ts Outdated
Comment on lines +135 to +149
/** Basic structural validation — not a full JSON Schema validator. */
function validateSource(raw: unknown, filePath: string): PirStatusFile {
if (typeof raw !== 'object' || raw === null) {
throw new Error(`${filePath}: not a JSON object`);
}
const obj = raw as Record<string, unknown>;
for (const key of ['schema_version', 'cycle', 'date', 'pirs'] as const) {
if (!(key in obj)) throw new Error(`${filePath}: missing required field '${key}'`);
}
if (obj['schema_version'] !== '1.0') {
throw new Error(`${filePath}: unsupported schema_version '${String(obj['schema_version'])}'`);
}
if (!Array.isArray(obj['pirs'])) {
throw new Error(`${filePath}: 'pirs' must be an array`);
}
Comment thread scripts/roll-forward-pirs.ts Outdated
Comment on lines +337 to +340
main(process.argv.slice(2)).catch((err: unknown) => {
console.error('Unexpected error:', err);
process.exit(1);
});
Comment on lines +88 to +92
"answer_summary": {
"type": "string",
"description": "Short (≤ 500 chars) summary of the evidence or event that answered this PIR. Populate only when status='answered'.",
"maxLength": 500
},
Comment thread schemas/pir-status.schema.json Outdated
},
"subfolder": {
"type": "string",
"description": "Relative subfolder path under `analysis/daily/YYYY-MM-DD/` — must equal the `cycle` value.",
@pethers
Copy link
Copy Markdown
Member

pethers commented Apr 27, 2026

@copilot apply changes based on the comments in this thread and fix TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)
TypeScript & JavaScript Testing / Unit Tests (Vitest) (pull_request)Failing after 2m

- Schema: add if/then/else for answer_summary; soften subfolder description
- roll-forward-pirs.ts: export helpers; isMainModule guard; degrade(-1) throws;
  preserve inherits_from for non-open PIRs; strict enum validation in
  validateSource; runMain takes injected IO for testability
- Analysis gate (Check 10): iterate all PIRs (comment matches behavior),
  enforce subfolder===cycle and conditional answer_summary
- Guide: clarify newly extracted PIRs default to open; rolled-forward
  preserve prior status
- Tests: 69 direct unit tests via imports (was 35 via execSync); use
  os.tmpdir() to avoid armour false-positives. Coverage now 84.59% (≥80%)

Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/1f753652-b2a1-48e7-bc4e-89aeed758265

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot apply changes based on the comments in this thread and fix [TypeScript & JavaScript Testing...

Addressed in 38f50c0. All 8 review threads resolved: schema now uses if/then/else for answer_summary and softens the subfolder description; roll-forward-pirs.ts exports its helpers behind an isMainModule guard, degrade() throws on unknown values instead of silently returning VERY HIGH, non-open PIRs keep their existing inherits_from history unchanged, and validateSource() now strictly checks each entry's pir_id/status/confidence enums and the conditional answer_summary invariant; gate Check 10 enforces subfolder===cycle and the conditional answer_summary, with the comment now matching the actual all-PIRs iteration; guide clarifies newly extracted PIRs default to open while rolled-forward entries preserve prior status. CI Vitest is also fixed — tests use os.tmpdir() (eliminating the step-security armour writes under repo tmp/) and exercise helpers via direct imports instead of execSync, lifting global statement coverage from 79.08% to 84.59% (full suite 2230/2230 pass).

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a machine-readable pir-status.json “sidecar” contract to enable PIR status roll-forward across analysis cycles, backed by a schema, a roll-forward CLI script, analysis-gate enforcement, and a dedicated test suite.

Changes:

  • Introduces schemas/pir-status.schema.json (v1.0) defining the sidecar structure and conditional answer_summary.
  • Adds scripts/roll-forward-pirs.ts to roll forward PIRs between cycles (degrade confidence for open PIRs, preserve non-open PIRs).
  • Updates analysis gate + methodology guide and adds a comprehensive contract/unit test suite.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
schemas/pir-status.schema.json Defines the JSON Schema contract for pir-status.json.
scripts/roll-forward-pirs.ts Implements roll-forward CLI + exported helpers for testing.
.github/prompts/05-analysis-gate.md Enforces presence/shape invariants for pir-status.json in Check 10.
analysis/methodologies/ai-driven-analysis-guide.md Documents the new required artifact and roll-forward workflow.
tests/pir-status-contract.test.ts Adds contract + unit tests for schema, script helpers, and gate text.

Comment thread scripts/roll-forward-pirs.ts Outdated
else if (arg === '--date') args.date = argv[++i];
else if (arg === '--cycle') args.cycle = argv[++i] as CycleType;
else if (arg === '--dry-run') args.dryRun = true;
else if (arg === '--max-lookback') args.maxLookback = parseInt(argv[++i] ?? '14', 10);
Comment thread scripts/roll-forward-pirs.ts Outdated
Comment on lines +278 to +290
const relativeSourcePath = sourcePath.startsWith(repoRoot + path.sep)
? sourcePath.slice(repoRoot.length + 1).replace(/\\/g, '/')
: sourcePath.replace(/\\/g, '/');

return {
schema_version: '1.0',
cycle: targetCycle,
date: targetDate,
subfolder: targetCycle,
generated_at: now().toISOString(),
inherited_from: relativeSourcePath,
pirs,
};
Comment thread .github/prompts/05-analysis-gate.md Outdated
Comment on lines +235 to +239
# Check 10 — PIR status sidecar (`pir-status.json`)
# A valid pir-status.json must be present after every analysis run so that
# open PIRs can be automatically rolled forward to the next cycle.
# Schema: schemas/pir-status.schema.json (v1.0)
# Roll-forward script: scripts/roll-forward-pirs.ts
Comment thread tests/pir-status-contract.test.ts Outdated
Comment on lines +807 to +814

it('runMain does not invoke exit when invoked with valid args', () => {
// Already covered by happy-path test, but assert the spy pattern is clean.
const exitSpy = vi.fn();
const noopOut = { write: () => true } as unknown as NodeJS.WritableStream;
expect(typeof exitSpy).toBe('function');
expect(typeof noopOut.write).toBe('function');
});
},
"answer_summary": {
"type": "string",
"description": "Short (≤ 500 chars) summary of the evidence or event that answered this PIR. Populate only when status='answered'.",
Comment on lines +180 to +195
export function validateSource(raw: unknown, filePath: string): PirStatusFile {
if (typeof raw !== 'object' || raw === null) {
throw new Error(`${filePath}: not a JSON object`);
}
const obj = raw as Record<string, unknown>;
for (const key of ['schema_version', 'cycle', 'date', 'pirs'] as const) {
if (!(key in obj)) throw new Error(`${filePath}: missing required field '${key}'`);
}
if (obj['schema_version'] !== '1.0') {
throw new Error(
`${filePath}: unsupported schema_version '${String(obj['schema_version'])}'`,
);
}
if (!Array.isArray(obj['pirs'])) {
throw new Error(`${filePath}: 'pirs' must be an array`);
}
@pethers
Copy link
Copy Markdown
Member

pethers commented Apr 27, 2026

@copilot apply changes based on the comments in this thread

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot apply changes based on the comments in this thread

Addressed in e69f8cf and follow-up f239bb0. The second review thread is resolved: --max-lookback now validates positive integers with clear exit-1 errors, inherited_from uses path.relative() with normalized separators, validateSource() checks the full top-level sidecar shape, schema answer_summary has minLength: 1, gate numbering is sequential (Check 9 PIR, Check 10 supplementary), and the misleading runMain test was removed while adding focused regression coverage. Local validation passes: targeted PIR tests 81/81, full Vitest coverage 2242/2242 with 84.77% statements, ESLint on changed TS files, and schema JSON validation.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Lighthouse Performance Audit

Category Score Status
Performance 85/100 🟡
Accessibility 95/100 🟢
Best Practices 90/100 🟢
SEO 95/100 🟢

📥 Download full Lighthouse report

Budget Compliance: Performance budgets enforced via budget.json

@pethers pethers merged commit d2fd995 into main Apr 27, 2026
17 checks passed
@pethers pethers deleted the copilot/add-pir-status-json-sidecar branch April 27, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Documentation updates refactor Code refactoring schema Data schema changes size-xl Extra large change (> 1000 lines) size-xs Extra small change (< 10 lines) testing Test coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Methodology] Machine-readable PIR status sidecar (pir-status.json) for cross-cycle inheritance

3 participants