flow-write-live-kn-b.test.mjs
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6
docs: record AIP-b SD-21 land (KN #308)
Human
9 days ago
| 1 | /** |
| 2 | * FLOW-WRITE-LIVE-KN-b — seven-tier coverage (§FWL.7 Knowtation matrix). |
| 3 | * Frozen: ~/scooling/docs/FLOW-WRITE-LIVE-FREEZE.md (§FWL.4 / §FWL.7) |
| 4 | * |
| 5 | * Tiers: unit · integration · e2e · stress · data-integrity · performance · security |
| 6 | */ |
| 7 | |
| 8 | import { describe, it, beforeEach, afterEach } from 'node:test'; |
| 9 | import assert from 'node:assert/strict'; |
| 10 | import fs from 'node:fs'; |
| 11 | import path from 'node:path'; |
| 12 | import { performance } from 'node:perf_hooks'; |
| 13 | import { fileURLToPath } from 'node:url'; |
| 14 | import { |
| 15 | personalSelfApplyRefusalReason, |
| 16 | isPersonalSelfApplyClass, |
| 17 | matchesScoolingFlowFingerprint, |
| 18 | isAdmittedSeamSelfApplyFingerprint, |
| 19 | applyPersonalSelfApplyEvaluationE1, |
| 20 | SCOOLING_FLOW_EXTERNAL_REF_RE, |
| 21 | SCOOLING_FLOW_MIRROR_PATH_RE, |
| 22 | ADMITTED_FLOW_PROPOSAL_KINDS, |
| 23 | } from '../lib/hub-proposal-personal-self-apply.mjs'; |
| 24 | import { |
| 25 | resolveOptionalScoolingExternalRef, |
| 26 | readProposeExternalRefRaw, |
| 27 | } from '../lib/scooling-external-ref.mjs'; |
| 28 | import { |
| 29 | FLOW_PROPOSAL_SOURCE, |
| 30 | handleFlowProposeRequest, |
| 31 | } from '../lib/flow/flow-authoring.mjs'; |
| 32 | import { FLOW_CAPTURE_PROPOSAL_SOURCE } from '../lib/flow/flow-capture.mjs'; |
| 33 | import { DELEGATION_PROPOSAL_SOURCE } from '../lib/agent/delegation.mjs'; |
| 34 | import { createProposal, getProposal } from '../hub/proposals-store.mjs'; |
| 35 | import { stripClientEvaluationFields } from '../lib/hub-proposal-create-augment.mjs'; |
| 36 | import { makeFlowBundle } from './fixtures/flow/authoring-helpers.mjs'; |
| 37 | |
| 38 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
| 39 | const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-flow-write-live-kn-b'); |
| 40 | |
| 41 | const ACTOR = 'google:learner-a'; |
| 42 | const OTHER = 'google:learner-b'; |
| 43 | |
| 44 | /** |
| 45 | * @param {Record<string, unknown>} [overrides] |
| 46 | */ |
| 47 | function flowProposal(overrides = {}) { |
| 48 | const kind = overrides.kind || 'new'; |
| 49 | const flowId = overrides.flowId || 'flow_fwl_1'; |
| 50 | const scope = overrides.scope || 'personal'; |
| 51 | const mirror = |
| 52 | overrides.path || |
| 53 | `meta/flows/${String(flowId).replace(/^flow_/, '').replace(/_/g, '-')}.md`; |
| 54 | const bodyObj = overrides.bodyObj || { |
| 55 | flow: { |
| 56 | schema: 'knowtation.flow/v0', |
| 57 | flow_id: flowId, |
| 58 | title: 'FWL', |
| 59 | version: '1.0.0', |
| 60 | scope, |
| 61 | summary: 'fwl', |
| 62 | tags: [], |
| 63 | steps: [], |
| 64 | inputs: [], |
| 65 | vault_mirror_path: mirror, |
| 66 | }, |
| 67 | steps: [], |
| 68 | }; |
| 69 | const hasMeta = Object.prototype.hasOwnProperty.call(overrides, 'flow_meta'); |
| 70 | const flowMeta = hasMeta |
| 71 | ? overrides.flow_meta |
| 72 | : { kind, base_version: null, base_state_id: 'flowst1_absent' }; |
| 73 | return { |
| 74 | proposal_id: overrides.proposal_id || 'prop-flow-1', |
| 75 | status: 'proposed', |
| 76 | source: FLOW_PROPOSAL_SOURCE, |
| 77 | path: mirror, |
| 78 | external_ref: overrides.external_ref ?? 'scooling.flow:fixture-001', |
| 79 | body: JSON.stringify(bodyObj), |
| 80 | frontmatter: { |
| 81 | type: 'flow', |
| 82 | flow_id: flowId, |
| 83 | flow_version: '1.0.0', |
| 84 | scope, |
| 85 | ...(overrides.frontmatter || {}), |
| 86 | }, |
| 87 | ...(flowMeta !== undefined ? { flow_meta: flowMeta } : {}), |
| 88 | ...Object.fromEntries( |
| 89 | Object.entries(overrides).filter( |
| 90 | ([k]) => |
| 91 | ![ |
| 92 | 'kind', |
| 93 | 'flowId', |
| 94 | 'scope', |
| 95 | 'bodyObj', |
| 96 | 'path', |
| 97 | 'external_ref', |
| 98 | 'flow_meta', |
| 99 | 'frontmatter', |
| 100 | 'proposal_id', |
| 101 | ].includes(k), |
| 102 | ), |
| 103 | ), |
| 104 | }; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * @param {Record<string, unknown>} proposal |
| 109 | * @param {Record<string, unknown>} [extra] |
| 110 | */ |
| 111 | function eligible(proposal, extra = {}) { |
| 112 | return { |
| 113 | proposal, |
| 114 | hasVaultWrite: true, |
| 115 | partitionOwned: true, |
| 116 | role: 'member', |
| 117 | humanActor: true, |
| 118 | tokenType: null, |
| 119 | actorKind: 'human', |
| 120 | sessionBound: true, |
| 121 | authorActorId: ACTOR, |
| 122 | approverActorId: ACTOR, |
| 123 | ...extra, |
| 124 | }; |
| 125 | } |
| 126 | |
| 127 | describe('FLOW-WRITE-LIVE-KN-b unit — §FWL.4.1 Flow fingerprint', () => { |
| 128 | it('admits new/edit/import + scooling.flow: + personal meta/flows', async () => { |
| 129 | for (const kind of ADMITTED_FLOW_PROPOSAL_KINDS) { |
| 130 | const p = flowProposal({ kind, external_ref: `scooling.flow:${kind}-ok` }); |
| 131 | assert.equal(matchesScoolingFlowFingerprint(p), true, kind); |
| 132 | assert.equal(personalSelfApplyRefusalReason(eligible(p)), null, kind); |
| 133 | } |
| 134 | assert.equal(SCOOLING_FLOW_MIRROR_PATH_RE.test('meta/flows/capture-to-note.md'), true); |
| 135 | assert.equal(SCOOLING_FLOW_EXTERNAL_REF_RE.test('scooling.flow:ok'), true); |
| 136 | }); |
| 137 | |
| 138 | it('refuses capture / delegation / wrong ref / empty author / project scope / missing kind', async () => { |
| 139 | assert.equal( |
| 140 | personalSelfApplyRefusalReason( |
| 141 | eligible({ status: 'proposed', source: FLOW_CAPTURE_PROPOSAL_SOURCE }), |
| 142 | ), |
| 143 | 'SELF_APPLY_NOT_ADMITTED', |
| 144 | ); |
| 145 | assert.equal( |
| 146 | personalSelfApplyRefusalReason( |
| 147 | eligible({ status: 'proposed', source: DELEGATION_PROPOSAL_SOURCE }), |
| 148 | ), |
| 149 | 'SELF_APPLY_DELEGATION_REFUSED', |
| 150 | ); |
| 151 | assert.equal( |
| 152 | personalSelfApplyRefusalReason( |
| 153 | eligible(flowProposal({ external_ref: 'scooling.task:wrong' })), |
| 154 | ), |
| 155 | 'SELF_APPLY_NOT_ADMITTED', |
| 156 | ); |
| 157 | assert.equal( |
| 158 | personalSelfApplyRefusalReason(eligible(flowProposal(), { authorActorId: '' })), |
| 159 | 'SELF_APPLY_AUTHOR_UNVERIFIED', |
| 160 | ); |
| 161 | assert.equal( |
| 162 | personalSelfApplyRefusalReason(eligible(flowProposal({ scope: 'project' }))), |
| 163 | 'SELF_APPLY_NOT_ADMITTED', |
| 164 | ); |
| 165 | assert.equal( |
| 166 | personalSelfApplyRefusalReason(eligible(flowProposal({ scope: 'org' }))), |
| 167 | 'SELF_APPLY_NOT_ADMITTED', |
| 168 | ); |
| 169 | // Missing flow_meta / empty kind — never default to new at admission. |
| 170 | assert.equal(matchesScoolingFlowFingerprint(flowProposal({ flow_meta: undefined })), false); |
| 171 | assert.equal( |
| 172 | matchesScoolingFlowFingerprint(flowProposal({ flow_meta: { kind: '', base_version: null } })), |
| 173 | false, |
| 174 | ); |
| 175 | assert.equal( |
| 176 | matchesScoolingFlowFingerprint( |
| 177 | flowProposal({ flow_meta: { kind: 'draft', base_version: null } }), |
| 178 | ), |
| 179 | false, |
| 180 | ); |
| 181 | assert.equal( |
| 182 | matchesScoolingFlowFingerprint( |
| 183 | flowProposal({ path: 'notes/flows/x.md', external_ref: 'scooling.flow:ok' }), |
| 184 | ), |
| 185 | false, |
| 186 | ); |
| 187 | }); |
| 188 | |
| 189 | it('external_ref helpers validate Flow regex; malformed refuse', async () => { |
| 190 | const bad = resolveOptionalScoolingExternalRef('muse:ref', SCOOLING_FLOW_EXTERNAL_REF_RE); |
| 191 | assert.equal(bad.ok, false); |
| 192 | assert.equal(bad.status, 400); |
| 193 | assert.equal(bad.code, 'EXTERNAL_REF_INVALID'); |
| 194 | const ok = resolveOptionalScoolingExternalRef('scooling.flow:a', SCOOLING_FLOW_EXTERNAL_REF_RE); |
| 195 | assert.equal(ok.ok, true); |
| 196 | assert.equal(ok.externalRef, 'scooling.flow:a'); |
| 197 | assert.equal( |
| 198 | readProposeExternalRefRaw({ body: { external_ref: 'scooling.flow:b' } }), |
| 199 | 'scooling.flow:b', |
| 200 | ); |
| 201 | }); |
| 202 | }); |
| 203 | |
| 204 | describe('FLOW-WRITE-LIVE-KN-b integration — propose persist + approve class', () => { |
| 205 | const dataDir = path.join(tmpRoot, 'integ'); |
| 206 | const visible = new Set(['personal', 'project', 'org']); |
| 207 | beforeEach(() => { |
| 208 | fs.rmSync(tmpRoot, { recursive: true, force: true }); |
| 209 | fs.mkdirSync(dataDir, { recursive: true }); |
| 210 | process.env.FLOW_AUTHORING_WRITES = '1'; |
| 211 | }); |
| 212 | afterEach(() => { |
| 213 | delete process.env.FLOW_AUTHORING_WRITES; |
| 214 | fs.rmSync(tmpRoot, { recursive: true, force: true }); |
| 215 | }); |
| 216 | |
| 217 | it('persists scooling.flow: on new propose; session-bound class holds', async () => { |
| 218 | const bundle = makeFlowBundle({ flowId: 'flow_fwl_persist' }); |
| 219 | const proposed = await handleFlowProposeRequest({ |
| 220 | dataDir, |
| 221 | vaultId: 'default', |
| 222 | userId: ACTOR, |
| 223 | visibleScopes: visible, |
| 224 | kind: 'new', |
| 225 | flow: bundle.flow, |
| 226 | steps: bundle.steps, |
| 227 | intent: 'draft personal flow', |
| 228 | externalRef: 'scooling.flow:persist-1', |
| 229 | sessionBound: true, |
| 230 | createProposal, |
| 231 | }); |
| 232 | assert.equal(proposed.ok, true); |
| 233 | const row = getProposal(dataDir, proposed.payload.proposal_id); |
| 234 | assert.equal(row.external_ref, 'scooling.flow:persist-1'); |
| 235 | assert.equal(row.source, FLOW_PROPOSAL_SOURCE); |
| 236 | assert.equal(row.flow_meta.kind, 'new'); |
| 237 | assert.match(row.path, /^meta\/flows\/.+\.md$/); |
| 238 | assert.equal(personalSelfApplyRefusalReason(eligible(row, { role: 'editor' })), null); |
| 239 | }); |
| 240 | |
| 241 | it('absent external_ref proposes ok but not admitted; malformed → 400', async () => { |
| 242 | const bundle = makeFlowBundle({ flowId: 'flow_fwl_noref' }); |
| 243 | const noRef = await handleFlowProposeRequest({ |
| 244 | dataDir, |
| 245 | vaultId: 'default', |
| 246 | userId: ACTOR, |
| 247 | visibleScopes: visible, |
| 248 | kind: 'new', |
| 249 | flow: bundle.flow, |
| 250 | steps: bundle.steps, |
| 251 | intent: 'no ref', |
| 252 | sessionBound: true, |
| 253 | createProposal, |
| 254 | }); |
| 255 | assert.equal(noRef.ok, true); |
| 256 | const row = getProposal(dataDir, noRef.payload.proposal_id); |
| 257 | assert.equal(personalSelfApplyRefusalReason(eligible(row)), 'SELF_APPLY_NOT_ADMITTED'); |
| 258 | |
| 259 | const badBundle = makeFlowBundle({ flowId: 'flow_fwl_badref' }); |
| 260 | const badNew = await handleFlowProposeRequest({ |
| 261 | dataDir, |
| 262 | vaultId: 'default', |
| 263 | userId: ACTOR, |
| 264 | visibleScopes: visible, |
| 265 | kind: 'new', |
| 266 | flow: badBundle.flow, |
| 267 | steps: badBundle.steps, |
| 268 | intent: 'bad ref', |
| 269 | externalRef: 'not-a-scooling-flow-ref', |
| 270 | createProposal, |
| 271 | }); |
| 272 | assert.equal(badNew.ok, false); |
| 273 | assert.equal(badNew.status, 400); |
| 274 | assert.equal(badNew.code, 'EXTERNAL_REF_INVALID'); |
| 275 | }); |
| 276 | |
| 277 | it('import kind persists scooling.flow:; lineage muse ref refused', async () => { |
| 278 | const bundle = makeFlowBundle({ flowId: 'flow_fwl_import' }); |
| 279 | const imported = await handleFlowProposeRequest({ |
| 280 | dataDir, |
| 281 | vaultId: 'default', |
| 282 | userId: ACTOR, |
| 283 | visibleScopes: visible, |
| 284 | kind: 'import', |
| 285 | bundle: { flow: bundle.flow, steps: bundle.steps }, |
| 286 | intent: 'import', |
| 287 | externalRef: 'scooling.flow:import-1', |
| 288 | sourceVaultHint: 'partner', |
| 289 | sessionBound: true, |
| 290 | createProposal, |
| 291 | }); |
| 292 | assert.equal(imported.ok, true); |
| 293 | const row = getProposal(dataDir, imported.payload.proposal_id); |
| 294 | assert.equal(row.flow_meta.kind, 'import'); |
| 295 | assert.equal(row.external_ref, 'scooling.flow:import-1'); |
| 296 | assert.equal(isAdmittedSeamSelfApplyFingerprint(row, ACTOR), true); |
| 297 | |
| 298 | const lineageBundle = makeFlowBundle({ flowId: 'flow_fwl_import_lineage' }); |
| 299 | const lineageOnly = await handleFlowProposeRequest({ |
| 300 | dataDir, |
| 301 | vaultId: 'default', |
| 302 | userId: ACTOR, |
| 303 | visibleScopes: visible, |
| 304 | kind: 'import', |
| 305 | bundle: { flow: lineageBundle.flow, steps: lineageBundle.steps }, |
| 306 | intent: 'import lineage', |
| 307 | externalRef: 'muse:ref-123', |
| 308 | createProposal, |
| 309 | }); |
| 310 | assert.equal(lineageOnly.ok, false); |
| 311 | assert.equal(lineageOnly.code, 'EXTERNAL_REF_INVALID'); |
| 312 | }); |
| 313 | }); |
| 314 | |
| 315 | describe('FLOW-WRITE-LIVE-KN-b e2e — personal draft without Hub eval hop', () => { |
| 316 | const dataDir = path.join(tmpRoot, 'e2e'); |
| 317 | const visible = new Set(['personal']); |
| 318 | beforeEach(() => { |
| 319 | fs.rmSync(tmpRoot, { recursive: true, force: true }); |
| 320 | fs.mkdirSync(dataDir, { recursive: true }); |
| 321 | process.env.FLOW_AUTHORING_WRITES = '1'; |
| 322 | }); |
| 323 | afterEach(() => { |
| 324 | delete process.env.FLOW_AUTHORING_WRITES; |
| 325 | fs.rmSync(tmpRoot, { recursive: true, force: true }); |
| 326 | }); |
| 327 | |
| 328 | it('create+E1+class holds for session-bound personal Flow new', async () => { |
| 329 | const bundle = makeFlowBundle({ flowId: 'flow_fwl_e2e' }); |
| 330 | const proposed = await handleFlowProposeRequest({ |
| 331 | dataDir, |
| 332 | vaultId: 'default', |
| 333 | userId: ACTOR, |
| 334 | visibleScopes: visible, |
| 335 | kind: 'new', |
| 336 | flow: bundle.flow, |
| 337 | steps: bundle.steps, |
| 338 | intent: 'e2e draft', |
| 339 | externalRef: 'scooling.flow:e2e-1', |
| 340 | sessionBound: true, |
| 341 | createProposal, |
| 342 | }); |
| 343 | assert.equal(proposed.ok, true); |
| 344 | const row = getProposal(dataDir, proposed.payload.proposal_id); |
| 345 | assert.equal(row.evaluation_status, 'passed'); |
| 346 | assert.equal(row.evaluated_by, ACTOR); |
| 347 | assert.equal(isPersonalSelfApplyClass(eligible(row, { role: 'editor' })), true); |
| 348 | }); |
| 349 | }); |
| 350 | |
| 351 | describe('FLOW-WRITE-LIVE-KN-b stress — N cycles no cross-user leakage', () => { |
| 352 | it('distinct users never share Flow admission', async () => { |
| 353 | for (let i = 0; i < 100; i++) { |
| 354 | const a = flowProposal({ |
| 355 | proposal_id: `prop-a-${i}`, |
| 356 | external_ref: `scooling.flow:a-${i}`, |
| 357 | flowId: `flow_a_${i}`, |
| 358 | }); |
| 359 | const b = flowProposal({ |
| 360 | proposal_id: `prop-b-${i}`, |
| 361 | external_ref: `scooling.flow:b-${i}`, |
| 362 | flowId: `flow_b_${i}`, |
| 363 | }); |
| 364 | assert.equal( |
| 365 | personalSelfApplyRefusalReason(eligible(a, { authorActorId: ACTOR, approverActorId: ACTOR })), |
| 366 | null, |
| 367 | ); |
| 368 | assert.equal( |
| 369 | personalSelfApplyRefusalReason(eligible(b, { authorActorId: ACTOR, approverActorId: OTHER })), |
| 370 | 'SELF_APPLY_AUTHOR_MISMATCH', |
| 371 | ); |
| 372 | } |
| 373 | }); |
| 374 | }); |
| 375 | |
| 376 | describe('FLOW-WRITE-LIVE-KN-b data-integrity — index fields + server audit', () => { |
| 377 | it('client evaluation_status stripped; E1 sets server audit on Flow fingerprint', async () => { |
| 378 | const stripped = stripClientEvaluationFields({ |
| 379 | evaluation_status: 'passed', |
| 380 | evaluated_by: 'forged', |
| 381 | evaluated_at: '2099-01-01T00:00:00.000Z', |
| 382 | path: 'meta/flows/fwl-di.md', |
| 383 | source: FLOW_PROPOSAL_SOURCE, |
| 384 | external_ref: 'scooling.flow:di-1', |
| 385 | body: JSON.stringify({ |
| 386 | flow: { flow_id: 'flow_fwl_di', scope: 'personal', version: '1.0.0' }, |
| 387 | steps: [], |
| 388 | }), |
| 389 | frontmatter: { type: 'flow', flow_id: 'flow_fwl_di', scope: 'personal' }, |
| 390 | flow_meta: { kind: 'new', base_version: null, base_state_id: 'x' }, |
| 391 | }); |
| 392 | assert.equal(stripped.evaluation_status, undefined); |
| 393 | const e1 = applyPersonalSelfApplyEvaluationE1(stripped, { |
| 394 | evaluatedBy: ACTOR, |
| 395 | sessionBound: true, |
| 396 | authorActorId: ACTOR, |
| 397 | evaluatedAt: '2026-07-28T00:00:00.000Z', |
| 398 | }); |
| 399 | assert.equal(e1.evaluation_status, 'passed'); |
| 400 | assert.equal(e1.evaluated_by, ACTOR); |
| 401 | assert.equal(e1.evaluated_at, '2026-07-28T00:00:00.000Z'); |
| 402 | assert.equal(e1.external_ref, 'scooling.flow:di-1'); |
| 403 | assert.equal(e1.flow_meta.kind, 'new'); |
| 404 | }); |
| 405 | }); |
| 406 | |
| 407 | describe('FLOW-WRITE-LIVE-KN-b performance — Flow admission bounded like Tasks class', () => { |
| 408 | it('1000 Flow fingerprint + E1 stamps stay under budget', async () => { |
| 409 | const body = { |
| 410 | path: 'meta/flows/perf.md', |
| 411 | source: FLOW_PROPOSAL_SOURCE, |
| 412 | external_ref: 'scooling.flow:perf', |
| 413 | body: JSON.stringify({ |
| 414 | flow: { flow_id: 'flow_perf', scope: 'personal', version: '1.0.0' }, |
| 415 | steps: [], |
| 416 | }), |
| 417 | frontmatter: { type: 'flow', scope: 'personal' }, |
| 418 | flow_meta: { kind: 'new', base_version: null, base_state_id: 'x' }, |
| 419 | }; |
| 420 | const t0 = performance.now(); |
| 421 | for (let i = 0; i < 1000; i++) { |
| 422 | assert.equal(matchesScoolingFlowFingerprint(body), true); |
| 423 | applyPersonalSelfApplyEvaluationE1(body, { |
| 424 | evaluatedBy: ACTOR, |
| 425 | sessionBound: true, |
| 426 | authorActorId: ACTOR, |
| 427 | }); |
| 428 | } |
| 429 | const ms = performance.now() - t0; |
| 430 | assert.ok(ms < 500, `Flow E1 1000 cycles took ${ms}ms`); |
| 431 | }); |
| 432 | }); |
| 433 | |
| 434 | describe('FLOW-WRITE-LIVE-KN-b security — scope / injection / P4 / secrets', () => { |
| 435 | it('scope widen refuse; injection inert; P4 delegation refused; no secrets in fingerprint', async () => { |
| 436 | assert.equal( |
| 437 | personalSelfApplyRefusalReason(eligible(flowProposal(), { partitionOwned: false })), |
| 438 | 'NOT_PARTITION_OWNED', |
| 439 | ); |
| 440 | assert.equal( |
| 441 | personalSelfApplyRefusalReason(eligible(flowProposal(), { sessionBound: false })), |
| 442 | 'SELF_APPLY_SESSION_BINDING_REQUIRED', |
| 443 | ); |
| 444 | assert.equal( |
| 445 | personalSelfApplyRefusalReason( |
| 446 | eligible({ status: 'proposed', source: DELEGATION_PROPOSAL_SOURCE }), |
| 447 | ), |
| 448 | 'SELF_APPLY_DELEGATION_REFUSED', |
| 449 | ); |
| 450 | const injected = flowProposal({ |
| 451 | external_ref: 'scooling.flow:ok"; DROP TABLE--', |
| 452 | }); |
| 453 | assert.equal(matchesScoolingFlowFingerprint(injected), false); |
| 454 | assert.equal( |
| 455 | personalSelfApplyRefusalReason(eligible(injected)), |
| 456 | 'SELF_APPLY_NOT_ADMITTED', |
| 457 | ); |
| 458 | const withSecretAttempt = flowProposal({ |
| 459 | bodyObj: { |
| 460 | flow: { |
| 461 | flow_id: 'flow_sec', |
| 462 | scope: 'personal', |
| 463 | version: '1.0.0', |
| 464 | api_key: 'sk-secret-should-not-affect-admission', |
| 465 | }, |
| 466 | steps: [], |
| 467 | }, |
| 468 | }); |
| 469 | assert.equal(matchesScoolingFlowFingerprint(withSecretAttempt), true); |
| 470 | assert.equal( |
| 471 | JSON.stringify(withSecretAttempt).includes('sk-secret'), |
| 472 | true, |
| 473 | 'fixture may carry inert body fields', |
| 474 | ); |
| 475 | // Admission uses path/source/kind/ref/scope only — secret body field does not elevate. |
| 476 | assert.equal(personalSelfApplyRefusalReason(eligible(withSecretAttempt)), null); |
| 477 | assert.equal( |
| 478 | personalSelfApplyRefusalReason( |
| 479 | eligible(flowProposal({ review_severity: 'elevated' })), |
| 480 | ), |
| 481 | 'ELEVATED_OR_AUTO_FLAGGED', |
| 482 | ); |
| 483 | }); |
| 484 | }); |
File History
1 commit
sha256:700fafdd1afa490919f9515d660ca6e75456bcd5bb67513abcd8757a634c01f6
docs: record AIP-b SD-21 land (KN #308)
Human
9 days ago