@maintainabilityai/research-runner 0.1.41 → 0.1.43
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/dist/runner/skills.js +68 -10
- package/package.json +1 -1
package/dist/runner/skills.js
CHANGED
|
@@ -1881,6 +1881,12 @@ const handleAuditVerifyChain = async (input) => {
|
|
|
1881
1881
|
// Track signature state across the whole chain.
|
|
1882
1882
|
let signedCount = 0;
|
|
1883
1883
|
let workflowUnsignedCount = 0; // post-agent workflow-emitted events, unsigned by-design
|
|
1884
|
+
// P9 (Bug-P / Codex audit): revise-agent unsigned events get their own
|
|
1885
|
+
// bucket so we can decide legitimacy chain-by-chain — legacy chains
|
|
1886
|
+
// (no per-epoch signing anywhere) keep the old allowance; per-epoch
|
|
1887
|
+
// chains (any event with `signer_epoch`) require revise-agent to sign.
|
|
1888
|
+
let reviseAgentUnsignedCount = 0;
|
|
1889
|
+
let chainUsesPerEpochSigning = false;
|
|
1884
1890
|
let prev = null;
|
|
1885
1891
|
for (let i = 0; i < lines.length; i++) {
|
|
1886
1892
|
let event;
|
|
@@ -1911,23 +1917,52 @@ const handleAuditVerifyChain = async (input) => {
|
|
|
1911
1917
|
if (recordedHash !== recomputed) {
|
|
1912
1918
|
return { ok: false, reason: `forged-hash-line-${i + 1}: recorded=${recordedHash.slice(0, 16)}… recomputed=${recomputed.slice(0, 16)}…` };
|
|
1913
1919
|
}
|
|
1914
|
-
// Bug K + N (cert-run-5): post-agent events
|
|
1915
|
-
//
|
|
1916
|
-
//
|
|
1917
|
-
// gone
|
|
1918
|
-
//
|
|
1919
|
-
//
|
|
1920
|
+
// Bug K + N (cert-run-5): post-agent events emitted by the workflow
|
|
1921
|
+
// (e.g. the synthetic self_review backfill that runs AFTER the agent
|
|
1922
|
+
// session ended) genuinely cannot sign — the ephemeral private key
|
|
1923
|
+
// is gone by then. `payload.emitted_by: 'workflow'` is the legitimate
|
|
1924
|
+
// unsigned attribution.
|
|
1925
|
+
//
|
|
1926
|
+
// P9 (Bug-P / Codex audit) — `revise-agent` used to share the same
|
|
1927
|
+
// legitimate-unsigned bucket because Bug N landed BEFORE Bug O. With
|
|
1928
|
+
// per-epoch signing (Bug O), a revise-agent session DOES have an
|
|
1929
|
+
// ephemeral key and DOES sign its events. So an unsigned revise-agent
|
|
1930
|
+
// event is now only legitimate on LEGACY chains — chains where no
|
|
1931
|
+
// event carries `signer_epoch`. Tracking that requires a chain-level
|
|
1932
|
+
// verdict, so we count unsigned revise-agent events into a separate
|
|
1933
|
+
// bucket and decide legitimacy after the loop sees whether the chain
|
|
1934
|
+
// uses per-epoch signing at all.
|
|
1920
1935
|
const eventPayload = event.payload;
|
|
1921
1936
|
const emittedBy = eventPayload?.emitted_by;
|
|
1922
|
-
const
|
|
1937
|
+
const isWorkflowUnsigned = emittedBy === 'workflow';
|
|
1938
|
+
const isReviseAgentUnsigned = emittedBy === 'revise-agent';
|
|
1939
|
+
if (typeof event.signer_epoch === 'number') {
|
|
1940
|
+
chainUsesPerEpochSigning = true;
|
|
1941
|
+
}
|
|
1923
1942
|
if (recordedSignature !== null && recordedSignature !== '') {
|
|
1924
1943
|
signedCount++;
|
|
1925
1944
|
}
|
|
1926
|
-
else if (
|
|
1945
|
+
else if (isWorkflowUnsigned) {
|
|
1927
1946
|
workflowUnsignedCount++;
|
|
1928
1947
|
}
|
|
1948
|
+
else if (isReviseAgentUnsigned) {
|
|
1949
|
+
reviseAgentUnsignedCount++;
|
|
1950
|
+
}
|
|
1929
1951
|
prev = recordedHash;
|
|
1930
1952
|
}
|
|
1953
|
+
// P9: legacy chains (pre-Bug-O — no signer_epoch on any event) keep
|
|
1954
|
+
// the broad allowance. New chains (any event carries signer_epoch)
|
|
1955
|
+
// require revise-agent events to be signed; an unsigned revise-agent
|
|
1956
|
+
// event on a per-epoch chain is now a real chain-integrity failure.
|
|
1957
|
+
if (chainUsesPerEpochSigning && reviseAgentUnsignedCount > 0) {
|
|
1958
|
+
return {
|
|
1959
|
+
ok: false,
|
|
1960
|
+
reason: `revise-agent-unsigned-on-per-epoch-chain: ${reviseAgentUnsignedCount} revise-agent events without signatures; per-epoch chains require revise-agent to sign with its own epoch key (Bug O contract)`,
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
// Legacy chains: roll revise-agent unsigned into the workflow-unsigned
|
|
1964
|
+
// bucket so the downstream "agent_event_count" math still excludes them.
|
|
1965
|
+
workflowUnsignedCount += reviseAgentUnsignedCount;
|
|
1931
1966
|
// Knight's Seal verification: every AGENT-emitted event must be signed
|
|
1932
1967
|
// by its declared signer_epoch's pub key. Workflow-unsigned events are
|
|
1933
1968
|
// excluded from the denominator (their emitted_by: 'workflow' marker
|
|
@@ -1935,6 +1970,24 @@ const handleAuditVerifyChain = async (input) => {
|
|
|
1935
1970
|
const sealed = signedCount > 0;
|
|
1936
1971
|
const agentEventCount = lines.length - workflowUnsignedCount;
|
|
1937
1972
|
let sealVerified = false;
|
|
1973
|
+
// Bug-Q / Q3 (Codex audit round 2) — a chain that USES per-epoch
|
|
1974
|
+
// signing (any event carries `signer_epoch`) MUST be sealed AND seal-
|
|
1975
|
+
// verified. Without this guard, an attacker could hand-craft a chain
|
|
1976
|
+
// where event 1 is signed (forcing `chainUsesPerEpochSigning=true`)
|
|
1977
|
+
// but every subsequent event is unsigned — `signedCount > 0` would
|
|
1978
|
+
// be true and the per-event check below would pass each unsigned
|
|
1979
|
+
// event as `legitimateUnsigned` if attribution were faked. Equally,
|
|
1980
|
+
// a chain where the runner reports `sealed=true` but the legacy
|
|
1981
|
+
// `chainUsesPerEpochSigning=false` path runs is the gold-product
|
|
1982
|
+
// promise we make to the marketing page. Legacy chains (no event
|
|
1983
|
+
// carries signer_epoch) keep the prior allowance — they predate
|
|
1984
|
+
// Bug O and a user audit-replaying them is intentionally tolerant.
|
|
1985
|
+
if (chainUsesPerEpochSigning && !sealed) {
|
|
1986
|
+
return {
|
|
1987
|
+
ok: false,
|
|
1988
|
+
reason: `per-epoch-chain-not-sealed: chain references signer_epoch (per-epoch signing contract) but no events carry signatures; gold-product contract requires per-epoch chains to be fully sealed`,
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1938
1991
|
if (sealed) {
|
|
1939
1992
|
if (signedCount !== agentEventCount) {
|
|
1940
1993
|
return { ok: false, reason: `partial-signatures: ${signedCount}/${agentEventCount} agent-emitted events signed (chain tampered; ${workflowUnsignedCount} workflow-emitted unsigned by-design)` };
|
|
@@ -1945,9 +1998,14 @@ const handleAuditVerifyChain = async (input) => {
|
|
|
1945
1998
|
for (let i = 0; i < lines.length; i++) {
|
|
1946
1999
|
const event = JSON.parse(lines[i]);
|
|
1947
2000
|
const emittedBy = event.payload?.emitted_by;
|
|
1948
|
-
|
|
2001
|
+
// P9: workflow-emitted unsigned events are always legitimate
|
|
2002
|
+
// (the post-agent context genuinely has no private key). For
|
|
2003
|
+
// revise-agent unsigned events, the loop above already returned
|
|
2004
|
+
// an error if we're on a per-epoch chain, so reaching this point
|
|
2005
|
+
// means we're on a legacy chain where the looser bucket applies.
|
|
2006
|
+
const isLegitimateUnsigned = (emittedBy === 'workflow' || emittedBy === 'revise-agent')
|
|
1949
2007
|
&& (!event.signature || event.signature === '');
|
|
1950
|
-
if (
|
|
2008
|
+
if (isLegitimateUnsigned) {
|
|
1951
2009
|
continue;
|
|
1952
2010
|
}
|
|
1953
2011
|
// Bug O (Task #72) — per-epoch verification. Events default to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maintainabilityai/research-runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.43",
|
|
4
4
|
"description": "Research + PRD agent runner — orchestrates the Archeologist and PRD pipelines for the MaintainabilityAI governance mesh",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "MaintainabilityAI",
|