rcf-lite 0.25.0 → 0.26.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/CHANGELOG.md +25 -0
- package/blueprints/application-account-settings/docs/topics.md +1 -1
- package/blueprints/application-admin-console/docs/topics.md +1 -1
- package/blueprints/application-api-rest/docs/topics.md +1 -1
- package/blueprints/application-charts/docs/topics.md +1 -1
- package/blueprints/application-dashboard/docs/topics.md +1 -1
- package/blueprints/application-datatable/docs/topics.md +1 -1
- package/blueprints/application-empty-error-states/docs/topics.md +1 -1
- package/blueprints/application-error-handling/docs/topics.md +1 -1
- package/blueprints/application-file-upload/docs/topics.md +1 -1
- package/blueprints/application-forms-wizard/docs/topics.md +1 -1
- package/blueprints/application-notifications-in-app/docs/topics.md +1 -1
- package/blueprints/application-onboarding-tour/docs/topics.md +1 -1
- package/blueprints/application-spa/docs/topics.md +1 -1
- package/blueprints/delivery-ci-workflows/docs/topics.md +1 -1
- package/blueprints/deploy-cloudflare-workers/docs/topics.md +1 -1
- package/blueprints/deploy-hetzner-server/CHANGELOG.md +21 -0
- package/blueprints/deploy-hetzner-server/blueprint.json +1 -1
- package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +12 -5
- package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +68 -20
- package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +8 -6
- package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +7 -1
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +2 -1
- package/blueprints/deploy-hetzner-server/contributions/templates/cloud-init.yaml.tmpl +26 -6
- package/blueprints/deploy-hetzner-server/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-access/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +10 -0
- package/blueprints/edge-cloudflare-tunnel/blueprint.json +1 -1
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/manifest-schema-validate.mjs +4 -1
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-connector-healthy.mjs +110 -21
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-tunnel-hostname-routes.mjs +123 -30
- package/blueprints/edge-cloudflare-tunnel/docs/topics.md +1 -1
- package/blueprints/edge-cloudflare-turnstile/docs/topics.md +1 -1
- package/blueprints/email-smtp-resend/docs/topics.md +1 -1
- package/blueprints/jobs-background/docs/topics.md +1 -1
- package/blueprints/messaging-queue-cloudflare/CHANGELOG.md +19 -0
- package/blueprints/messaging-queue-cloudflare/README.md +2 -2
- package/blueprints/messaging-queue-cloudflare/blueprint.json +1 -1
- package/blueprints/messaging-queue-cloudflare/contributions/probes/real-account-concurrency-smoke.mjs +192 -37
- package/blueprints/messaging-queue-cloudflare/docs/topics.md +1 -1
- package/blueprints/object-storage-s3/CHANGELOG.md +11 -0
- package/blueprints/object-storage-s3/README.md +14 -1
- package/blueprints/object-storage-s3/blueprint.json +12 -1
- package/blueprints/object-storage-s3/contributions/adrs/adr-2905-object-storage-s3-hetzner-object-storage-provider.json +30 -0
- package/blueprints/object-storage-s3/contributions/probes/hetzner-object-storage-round-trip.mjs +136 -0
- package/blueprints/object-storage-s3/contributions/probes/run-hetzner-object-storage-round-trip.mjs +11 -0
- package/blueprints/object-storage-s3/contributions/requirements/object-storage-s3-req-101.json +18 -0
- package/blueprints/object-storage-s3/contributions/tacs/tac-2904-object-storage-s3-hetzner-endpoint-helper.json +34 -0
- package/blueprints/object-storage-s3/contributions/user-stories/object-storage-s3-us-28110.json +25 -0
- package/blueprints/object-storage-s3/docs/topics.md +1 -1
- package/blueprints/object-storage-s3/guide/object-storage-s3.md +36 -0
- package/blueprints/observability-essentials/docs/topics.md +1 -1
- package/blueprints/observability-logging/docs/topics.md +1 -1
- package/blueprints/observability-probe-endpoints/docs/topics.md +1 -1
- package/blueprints/persistence-data-d1/docs/topics.md +1 -1
- package/blueprints/persistence-data-postgres/docs/topics.md +1 -1
- package/blueprints/persistence-data-sqlite/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +15 -0
- package/blueprints/platform-cloudflare-durable-objects/blueprint.json +1 -1
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/namespace-facade-ready.mjs +3 -3
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/probe-utils.mjs +28 -14
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/real-account-storage-smoke.mjs +132 -26
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/sole-reader-scan.mjs +40 -39
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/storage-round-trip.mjs +26 -16
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/websocket-hub-broadcast.mjs +31 -34
- package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +1 -1
- package/blueprints/platform-cloudflare-kv/CHANGELOG.md +40 -1
- package/blueprints/platform-cloudflare-kv/README.md +41 -13
- package/blueprints/platform-cloudflare-kv/blueprint.json +1 -1
- package/blueprints/platform-cloudflare-kv/contributions/probes/cache-aside-hit-then-miss.mjs +26 -26
- package/blueprints/platform-cloudflare-kv/contributions/probes/event-secrecy.mjs +49 -31
- package/blueprints/platform-cloudflare-kv/contributions/probes/facade-round-trip.mjs +58 -16
- package/blueprints/platform-cloudflare-kv/contributions/probes/list-with-prefix.mjs +6 -5
- package/blueprints/platform-cloudflare-kv/contributions/probes/real-account-eventual-consistency-smoke.mjs +131 -67
- package/blueprints/platform-cloudflare-kv/docs/topics.md +1 -1
- package/blueprints/platform-docker-compose-host/docs/topics.md +1 -1
- package/blueprints/security-auth-clerk/docs/topics.md +1 -1
- package/blueprints/security-auth-keycloak/docs/topics.md +1 -1
- package/blueprints/security-auth-magic-link/docs/topics.md +1 -1
- package/blueprints/security-auth-oauth2/docs/topics.md +1 -1
- package/blueprints/security-secrets-management/docs/topics.md +1 -1
- package/fixtures/canary-manifest.json +9 -9
- package/guidance/harness-template.md +67 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +67 -0
- package/package.json +2 -2
- package/rcf/code-nodes/cn-500.json +12 -0
- package/rcf/code-nodes/cn-510.json +12 -0
- package/rcf/code-nodes/cn-520.json +14 -0
- package/rcf/code-nodes/cn-521.json +12 -0
- package/rcf/code-nodes/cn-522.json +13 -0
- package/rcf/code-nodes/cn-523.json +12 -0
- package/rcf/code-nodes/cn-524.json +14 -0
- package/rcf/code-nodes/cn-525.json +12 -0
- package/rcf/code-nodes/cn-526.json +14 -0
- package/rcf/fbs/fbs-160.json +15 -0
- package/rcf/fbs/fbs-165.json +15 -0
- package/rcf/fbs/fbs-170.json +16 -0
- package/rcf/fbs/fbs-171.json +16 -0
- package/rcf/fbs/fbs-172.json +15 -0
- package/rcf/fbs/fbs-173.json +17 -0
- package/rcf/fbs/fbs-174.json +17 -0
- package/rcf/requirements/req-140.json +149 -0
- package/rcf/requirements/req-145.json +53 -0
- package/rcf/requirements/req-150.json +20 -0
- package/rcf/requirements/req-151.json +85 -0
- package/rcf/requirements/req-152.json +20 -0
- package/rcf/requirements/req-153.json +59 -0
- package/rcf/requirements/req-154.json +44 -0
- package/rcf/test-suites/ts-071.json +2 -2
- package/rcf/test-suites/ts-086.json +2 -2
- package/rcf/test-suites/ts-170.json +22 -0
- package/rcf/test-suites/ts-175.json +22 -0
- package/rcf/test-suites/ts-180.json +30 -0
- package/rcf/test-suites/ts-181.json +30 -0
- package/rcf/test-suites/ts-182.json +22 -0
- package/rcf/test-suites/ts-183.json +38 -0
- package/rcf/test-suites/ts-184.json +38 -0
- package/rcf/user-stories/us-14001.json +21 -0
- package/rcf/user-stories/us-14501.json +24 -0
- package/rcf/user-stories/us-15001.json +26 -0
- package/rcf/user-stories/us-15101.json +26 -0
- package/rcf/user-stories/us-15201.json +21 -0
- package/rcf/user-stories/us-15301.json +31 -0
- package/rcf/user-stories/us-15401.json +31 -0
- package/releases/releases.yaml +11 -1
- package/src/blueprint/apply.js +64 -0
- package/src/blueprint/consistency-lint.js +432 -0
- package/src/blueprint/disposition-ledger.js +224 -0
- package/src/blueprint/dispositions.js +44 -0
- package/src/blueprint/index.js +19 -0
- package/src/browser-verify/manifest-writer.js +53 -2
- package/src/browser-verify/pack-runner.js +18 -0
- package/src/cli/blueprint.js +103 -0
- package/src/setup/identity-seed.js +18 -0
|
@@ -7,15 +7,33 @@
|
|
|
7
7
|
// events fire on the paired operations, then deletes the key and
|
|
8
8
|
// asserts kvMiss fires on a subsequent read.
|
|
9
9
|
//
|
|
10
|
-
// Also covers AC-
|
|
11
|
-
// prefix pass over 10 keys under flags/ so the round-trip
|
|
12
|
-
// carries the compact case for the list mechanism.
|
|
10
|
+
// Also covers AC-31104-1 by extending the same run with a list-
|
|
11
|
+
// with-prefix pass over 10 keys under flags/ so the round-trip
|
|
12
|
+
// probe carries the compact case for the list mechanism.
|
|
13
13
|
//
|
|
14
|
-
//
|
|
15
|
-
// as
|
|
14
|
+
// AC-31102-1 (env.CACHE dereferenced in exactly one fixture source
|
|
15
|
+
// file) is carried here as a runtime observable: the probe reads
|
|
16
|
+
// the fixture src/ tree at run time and asserts exactly one file
|
|
17
|
+
// (src/kv-facade.mjs) contains the env.CACHE substring. This gives
|
|
18
|
+
// the sole-reader-grep AC a runtime observable in a probe result
|
|
19
|
+
// alongside the anatomy-test assertion (dispatch addendum, first
|
|
20
|
+
// dispatch groundwork note 2).
|
|
21
|
+
//
|
|
22
|
+
// anchorAcId: AC-31103-1 (primary: put/get round-trip and paired
|
|
23
|
+
// events per US-31103). Additional results cover AC-31101-1
|
|
24
|
+
// (facadeReady on boot), AC-31103-2 (delete then kvMiss),
|
|
25
|
+
// AC-31104-1 (list-with-prefix compact case) and AC-31102-1
|
|
26
|
+
// (sole-reader-grep runtime observable).
|
|
16
27
|
// accountBound: false.
|
|
17
28
|
|
|
18
|
-
|
|
29
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
30
|
+
import { dirname, resolve } from 'node:path';
|
|
31
|
+
import { fileURLToPath } from 'node:url';
|
|
32
|
+
|
|
33
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const FIXTURE_SRC = resolve(HERE, '..', '..', '..', '..', 'packages', 'rcf-lite', 'test', 'fixtures', 'cf-platform', 'src');
|
|
35
|
+
|
|
36
|
+
export const anchorAcId = 'AC-31103-1';
|
|
19
37
|
export const accountBound = false;
|
|
20
38
|
|
|
21
39
|
export default async function runProbe() {
|
|
@@ -29,20 +47,21 @@ export default async function runProbe() {
|
|
|
29
47
|
|
|
30
48
|
const results = [];
|
|
31
49
|
|
|
32
|
-
// AC-
|
|
33
|
-
// payload. Not the anchor here
|
|
34
|
-
//
|
|
50
|
+
// AC-31101-1 seam: ready() fires facadeReady with metadata-only
|
|
51
|
+
// payload. Not the primary anchor here, but the signal is worth
|
|
52
|
+
// surfacing on this probe as an additional result.
|
|
35
53
|
await facade.ready();
|
|
36
54
|
const ready = events.filter((e) => e.event === 'facadeReady');
|
|
37
55
|
results.push({
|
|
38
|
-
anchorAcId: 'AC-
|
|
56
|
+
anchorAcId: 'AC-31101-1',
|
|
39
57
|
verdict: ready.length === 1 && ready[0].key === null && ready[0].size === 0 ? 'pass' : 'fail',
|
|
40
58
|
detail: ready.length === 1
|
|
41
59
|
? `facadeReady fired once with key=${ready[0].key} size=${ready[0].size} ttl=${ready[0].ttl} timestamp=${ready[0].timestamp}`
|
|
42
60
|
: `facadeReady expected once, observed ${ready.length}`,
|
|
43
61
|
});
|
|
44
62
|
|
|
45
|
-
// AC-
|
|
63
|
+
// AC-31103-1: put/get round-trip returns same bytes and pairs
|
|
64
|
+
// paired kvWrite / kvHit events.
|
|
46
65
|
const key = 'kv-facade-round-trip/hello';
|
|
47
66
|
const value = 'hello workers-kv';
|
|
48
67
|
await facade.put(key, value, { metadata: { v: 1 } });
|
|
@@ -51,27 +70,29 @@ export default async function runProbe() {
|
|
|
51
70
|
const hitEvents = events.filter((e) => e.event === 'kvHit');
|
|
52
71
|
const roundTripPass = got === value && writeEvents.length === 1 && hitEvents.length === 1;
|
|
53
72
|
results.push({
|
|
54
|
-
anchorAcId: 'AC-
|
|
73
|
+
anchorAcId: 'AC-31103-1',
|
|
55
74
|
verdict: roundTripPass ? 'pass' : 'fail',
|
|
56
75
|
detail: roundTripPass
|
|
57
76
|
? `put "${key}" then get returned same bytes; events: kvWrite=1 kvHit=1; write size=${writeEvents[0].size}`
|
|
58
77
|
: `put/get mismatch: got=${JSON.stringify(got)} kvWrite=${writeEvents.length} kvHit=${hitEvents.length}`,
|
|
59
78
|
});
|
|
60
79
|
|
|
61
|
-
// AC-
|
|
80
|
+
// AC-31103-2: delete then get returns null and emits kvMiss.
|
|
62
81
|
await facade.delete(key);
|
|
63
82
|
const afterDelete = await facade.get(key);
|
|
64
83
|
const missEvents = events.filter((e) => e.event === 'kvMiss');
|
|
65
84
|
const deletePass = afterDelete === null && missEvents.length >= 1;
|
|
66
85
|
results.push({
|
|
67
|
-
anchorAcId: 'AC-
|
|
86
|
+
anchorAcId: 'AC-31103-2',
|
|
68
87
|
verdict: deletePass ? 'pass' : 'fail',
|
|
69
88
|
detail: deletePass
|
|
70
89
|
? `delete then get returned null; kvMiss events observed=${missEvents.length}`
|
|
71
90
|
: `delete branch fault: afterDelete=${JSON.stringify(afterDelete)} kvMiss=${missEvents.length}`,
|
|
72
91
|
});
|
|
73
92
|
|
|
74
|
-
// AC-
|
|
93
|
+
// AC-31104-1: list with prefix returns all 10 keys with metadata
|
|
94
|
+
// shape. Extended compact case on this probe; list-with-prefix
|
|
95
|
+
// probe holds the primary anchor.
|
|
75
96
|
const listBinding = createInMemoryKv();
|
|
76
97
|
const listEvents = [];
|
|
77
98
|
const listFacade = createKvFacade({ binding: listBinding, eventSink: (rec) => listEvents.push(rec) });
|
|
@@ -81,12 +102,33 @@ export default async function runProbe() {
|
|
|
81
102
|
const listed = await listFacade.list({ prefix: 'flags/' });
|
|
82
103
|
const listOk = listed.keys.length === 10 && listed.keys.every((k) => typeof k.name === 'string' && k.name.startsWith('flags/') && k.metadata && typeof k.metadata === 'object');
|
|
83
104
|
results.push({
|
|
84
|
-
anchorAcId: 'AC-
|
|
105
|
+
anchorAcId: 'AC-31104-1',
|
|
85
106
|
verdict: listOk ? 'pass' : 'fail',
|
|
86
107
|
detail: listOk
|
|
87
108
|
? `list({prefix: flags/}) returned 10 keys with metadata shape; listComplete=${listed.listComplete}`
|
|
88
109
|
: `list-with-prefix fault: keyCount=${listed.keys.length} sample=${JSON.stringify(listed.keys[0])}`,
|
|
89
110
|
});
|
|
90
111
|
|
|
112
|
+
// AC-31102-1 runtime observable: grep the fixture src/ tree for
|
|
113
|
+
// env.CACHE. The dereference must appear in exactly one file
|
|
114
|
+
// (src/kv-facade.mjs); the raw KV binding must not leak into any
|
|
115
|
+
// other module (or the future adapter swap would need to move
|
|
116
|
+
// multiple call sites).
|
|
117
|
+
const entries = await readdir(FIXTURE_SRC);
|
|
118
|
+
const hits = [];
|
|
119
|
+
for (const name of entries) {
|
|
120
|
+
if (!name.endsWith('.mjs')) continue;
|
|
121
|
+
const text = await readFile(resolve(FIXTURE_SRC, name), 'utf8');
|
|
122
|
+
if (text.includes('env.CACHE')) hits.push(name);
|
|
123
|
+
}
|
|
124
|
+
const soleReaderPass = hits.length === 1 && hits[0] === 'kv-facade.mjs';
|
|
125
|
+
results.push({
|
|
126
|
+
anchorAcId: 'AC-31102-1',
|
|
127
|
+
verdict: soleReaderPass ? 'pass' : 'fail',
|
|
128
|
+
detail: soleReaderPass
|
|
129
|
+
? `env.CACHE dereferenced in exactly one fixture src/ module: ${hits[0]}`
|
|
130
|
+
: `sole-reader-grep fault: expected exactly kv-facade.mjs to dereference env.CACHE; observed ${JSON.stringify(hits)}`,
|
|
131
|
+
});
|
|
132
|
+
|
|
91
133
|
return { results };
|
|
92
134
|
}
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
// Puts 10 keys under the shared prefix flags/ through the facade,
|
|
4
4
|
// calls facade.list({prefix: flags/}), asserts all 10 return with
|
|
5
5
|
// correct metadata shape. Independent of the round-trip probe; the
|
|
6
|
-
// round-trip probe also covers this AC
|
|
7
|
-
// mechanism as a first-class case for
|
|
6
|
+
// round-trip probe also covers this AC as an additional result but
|
|
7
|
+
// this probe carries the mechanism as a first-class case for
|
|
8
|
+
// US-31104.
|
|
8
9
|
//
|
|
9
|
-
// anchorAcId: AC-
|
|
10
|
+
// anchorAcId: AC-31104-1.
|
|
10
11
|
// accountBound: false.
|
|
11
12
|
|
|
12
|
-
export const anchorAcId = 'AC-
|
|
13
|
+
export const anchorAcId = 'AC-31104-1';
|
|
13
14
|
export const accountBound = false;
|
|
14
15
|
|
|
15
16
|
export default async function runProbe() {
|
|
@@ -42,7 +43,7 @@ export default async function runProbe() {
|
|
|
42
43
|
|
|
43
44
|
return {
|
|
44
45
|
results: [{
|
|
45
|
-
anchorAcId: 'AC-
|
|
46
|
+
anchorAcId: 'AC-31104-1',
|
|
46
47
|
verdict: pass ? 'pass' : 'fail',
|
|
47
48
|
detail: pass
|
|
48
49
|
? `list({prefix: flags/}) returned 10 keys under the prefix; every entry carries {name, metadata} with metadata.v=1; decoy key outside prefix was NOT included`
|
|
@@ -1,102 +1,166 @@
|
|
|
1
1
|
// Real-account eventual-consistency smoke for platform-cloudflare-kv
|
|
2
|
-
// v1.
|
|
2
|
+
// v1.1.0.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// key
|
|
4
|
+
// Self-provisioning: the probe mints its own scratch KV namespace via
|
|
5
|
+
// the fixture shim (packages/rcf-lite/test/fixtures/cf-platform/
|
|
6
|
+
// h2-cf-kv-real-account-shim.mjs), writes a fixture key, waits up to
|
|
7
|
+
// 60 seconds bounded (poll every 2s) for the same-region read to see
|
|
8
|
+
// the write, then deletes the key AND destroys the scratch namespace.
|
|
9
|
+
// Positive evidence captured: the CF-assigned namespace id, the exact
|
|
10
|
+
// scratch title used (H-2 prefix "h2-cf-probe-integrity-scratch-kv-"),
|
|
11
|
+
// the scratch key (H-2 prefix "h2-storage-smoke-"), elapsed milli-
|
|
12
|
+
// seconds to first same-region visibility, and the PUT status code.
|
|
13
|
+
// The teardown is fail-safe: a mid-run crash surfaces the scratch id
|
|
14
|
+
// through the shim's persisted record; the shim's sweepOrphans entry
|
|
15
|
+
// point cleans any residue by listing over the account and filtering
|
|
16
|
+
// on the throwaway prefix.
|
|
12
17
|
//
|
|
13
|
-
// accountBound: true. Without CI_HAS_CLOUDFLARE_ACCOUNT
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
18
|
+
// accountBound: true. Without CI_HAS_CLOUDFLARE_ACCOUNT the probe
|
|
19
|
+
// records accountBoundSkipped: true and the aggregate flips to pass
|
|
20
|
+
// per spec section 3.5. With CI_HAS_CLOUDFLARE_ACCOUNT=true the probe
|
|
21
|
+
// either produces positive evidence of execution or fails; no third
|
|
22
|
+
// outcome (dispatch requirement 5).
|
|
17
23
|
//
|
|
18
|
-
//
|
|
24
|
+
// Declared env (dispatch requirement 4): every env var that can cause
|
|
25
|
+
// a skip or an early return is enumerated in report.extra.envDeclared
|
|
26
|
+
// and mirrored in the fixture shim's DECLARED_ENV export. If a new
|
|
27
|
+
// gate is added and does not appear in DECLARED_ENV the anatomy test
|
|
28
|
+
// fails.
|
|
29
|
+
//
|
|
30
|
+
// anchorAcId: AC-31103-1. The eventual-consistency round-trip proves
|
|
31
|
+
// the shipped put/get shape on a real Cloudflare KV namespace,
|
|
32
|
+
// re-covering AC-31103-1 (facade put then get returns the same bytes
|
|
33
|
+
// with paired kvWrite / kvHit events on the shipped binding contract).
|
|
34
|
+
//
|
|
35
|
+
// Local-proof scope (Dave ruling 376b4f30): the local test harness
|
|
36
|
+
// under packages/rcf-lite/test/fixtures/cf-platform/test/ is a mock
|
|
37
|
+
// of Cloudflare's REST contract, not the wire. The local run
|
|
38
|
+
// exercises OUR lifecycle logic against a mock of Cloudflare's
|
|
39
|
+
// contract; the real-account gate is the only surface that proves
|
|
40
|
+
// the wire format. This probe is never described as "locally
|
|
41
|
+
// verified" - the local runs are our own lifecycle-logic proof;
|
|
42
|
+
// wire correctness is proven at the HQ real-account gate.
|
|
43
|
+
|
|
44
|
+
import {
|
|
45
|
+
mintScratchNamespace, destroyScratchNamespace,
|
|
46
|
+
putScratchKey, getScratchKey, deleteScratchKey,
|
|
47
|
+
generateKey, DECLARED_ENV, NAMESPACE_PREFIX, KEY_PREFIX,
|
|
48
|
+
} from '../../../../packages/rcf-lite/test/fixtures/cf-platform/h2-cf-kv-real-account-shim.mjs';
|
|
19
49
|
|
|
20
|
-
export const anchorAcId = 'AC-
|
|
50
|
+
export const anchorAcId = 'AC-31103-1';
|
|
21
51
|
export const accountBound = true;
|
|
22
52
|
|
|
23
53
|
const CAP_MS = 60_000;
|
|
24
54
|
const POLL_INTERVAL_MS = 2_000;
|
|
25
|
-
|
|
55
|
+
|
|
56
|
+
function skipResult(detail) {
|
|
57
|
+
return {
|
|
58
|
+
results: [{
|
|
59
|
+
anchorAcId: 'AC-31103-1',
|
|
60
|
+
verdict: 'pass',
|
|
61
|
+
detail,
|
|
62
|
+
accountBoundSkipped: true,
|
|
63
|
+
}],
|
|
64
|
+
extra: {
|
|
65
|
+
accountBoundSkipped: true,
|
|
66
|
+
envDeclared: Array.from(DECLARED_ENV),
|
|
67
|
+
throwawayPrefixes: { namespaceTitle: NAMESPACE_PREFIX, kvKey: KEY_PREFIX },
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
26
71
|
|
|
27
72
|
export default async function runProbe() {
|
|
28
73
|
if (process.env.CI_HAS_CLOUDFLARE_ACCOUNT !== 'true') {
|
|
74
|
+
return skipResult('accountBoundSkipped: CI_HAS_CLOUDFLARE_ACCOUNT is not set to true; spec section 3.5 pass-with-skip. Declared env for a live run: CI_HAS_CLOUDFLARE_ACCOUNT + CF_ACCOUNT_ID + CF_API_TOKEN (CF_API_BASE_URL optional test override).');
|
|
75
|
+
}
|
|
76
|
+
if (!process.env.CF_ACCOUNT_ID || !process.env.CF_API_TOKEN) {
|
|
29
77
|
return {
|
|
30
78
|
results: [{
|
|
31
|
-
anchorAcId: 'AC-
|
|
32
|
-
verdict: '
|
|
33
|
-
detail:
|
|
79
|
+
anchorAcId: 'AC-31103-1',
|
|
80
|
+
verdict: 'fail',
|
|
81
|
+
detail: `CI_HAS_CLOUDFLARE_ACCOUNT=true but one of CF_ACCOUNT_ID / CF_API_TOKEN is missing: accountIdPresent=${!!process.env.CF_ACCOUNT_ID} tokenPresent=${!!process.env.CF_API_TOKEN}.`,
|
|
34
82
|
}],
|
|
35
|
-
extra: {
|
|
83
|
+
extra: {
|
|
84
|
+
envDeclared: Array.from(DECLARED_ENV),
|
|
85
|
+
throwawayPrefixes: { namespaceTitle: NAMESPACE_PREFIX, kvKey: KEY_PREFIX },
|
|
86
|
+
},
|
|
36
87
|
};
|
|
37
88
|
}
|
|
38
89
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
90
|
+
const runId = process.env.GITHUB_RUN_ID || `local-${Date.now()}`;
|
|
91
|
+
let namespace = null;
|
|
92
|
+
const evidence = {
|
|
93
|
+
envDeclared: Array.from(DECLARED_ENV),
|
|
94
|
+
throwawayPrefixes: { namespaceTitle: NAMESPACE_PREFIX, kvKey: KEY_PREFIX },
|
|
95
|
+
runId,
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
namespace = await mintScratchNamespace({ runId });
|
|
99
|
+
evidence.namespaceId = namespace.id;
|
|
100
|
+
evidence.namespaceTitle = namespace.title;
|
|
101
|
+
} catch (err) {
|
|
43
102
|
return {
|
|
44
103
|
results: [{
|
|
45
|
-
anchorAcId: 'AC-
|
|
104
|
+
anchorAcId: 'AC-31103-1',
|
|
46
105
|
verdict: 'fail',
|
|
47
|
-
detail: `
|
|
106
|
+
detail: `mintScratchNamespace failed: ${err.message}`,
|
|
48
107
|
}],
|
|
108
|
+
extra: evidence,
|
|
49
109
|
};
|
|
50
110
|
}
|
|
51
111
|
|
|
52
|
-
const key =
|
|
112
|
+
const key = generateKey({ runId });
|
|
53
113
|
const value = `smoke-${Math.random().toString(36).slice(2)}`;
|
|
54
|
-
|
|
55
|
-
|
|
114
|
+
evidence.key = key;
|
|
115
|
+
evidence.valueSample = value;
|
|
56
116
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
results: [{
|
|
61
|
-
anchorAcId: 'AC-5401-1',
|
|
62
|
-
verdict: 'fail',
|
|
63
|
-
detail: `real-account smoke: PUT returned status ${putResp.status}`,
|
|
64
|
-
}],
|
|
65
|
-
};
|
|
66
|
-
}
|
|
117
|
+
try {
|
|
118
|
+
const put = await putScratchKey({ namespaceId: namespace.id, key, value });
|
|
119
|
+
evidence.putStatus = put.status;
|
|
67
120
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
observed = body;
|
|
121
|
+
const start = Date.now();
|
|
122
|
+
let observed = null;
|
|
123
|
+
while (Date.now() - start < CAP_MS) {
|
|
124
|
+
const got = await getScratchKey({ namespaceId: namespace.id, key });
|
|
125
|
+
if (got.ok && got.text === value) {
|
|
126
|
+
observed = got.text;
|
|
127
|
+
evidence.getStatusOnHit = got.status;
|
|
76
128
|
break;
|
|
77
129
|
}
|
|
130
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
78
131
|
}
|
|
79
|
-
|
|
80
|
-
|
|
132
|
+
const elapsed = Date.now() - start;
|
|
133
|
+
evidence.elapsedMs = elapsed;
|
|
134
|
+
evidence.capMs = CAP_MS;
|
|
81
135
|
|
|
82
|
-
|
|
136
|
+
// Delete the scratch key regardless of outcome; the namespace
|
|
137
|
+
// destroy in the finally block would remove it anyway, but the
|
|
138
|
+
// per-key delete proves the delete verb on the shipped path.
|
|
139
|
+
try {
|
|
140
|
+
const del = await deleteScratchKey({ namespaceId: namespace.id, key });
|
|
141
|
+
evidence.deleteStatus = del.status;
|
|
142
|
+
} catch (err) {
|
|
143
|
+
evidence.deleteError = err.message;
|
|
144
|
+
}
|
|
83
145
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
146
|
+
const pass = observed === value;
|
|
147
|
+
return {
|
|
148
|
+
results: [{
|
|
149
|
+
anchorAcId: 'AC-31103-1',
|
|
150
|
+
verdict: pass ? 'pass' : 'fail',
|
|
151
|
+
detail: pass
|
|
152
|
+
? `real-account KV eventual-consistency: mint namespace ${namespace.id} (${namespace.title}); PUT key ${key} (status ${evidence.putStatus}); write eventually appeared on same-region GET within elapsed=${elapsed}ms (CAP=${CAP_MS}ms); DELETE key (status ${evidence.deleteStatus ?? 'unset'}); namespace destroyed on teardown.`
|
|
153
|
+
: `real-account KV eventual-consistency: mint namespace ${namespace.id} (${namespace.title}); PUT key ${key} (status ${evidence.putStatus}); write did NOT appear within CAP=${CAP_MS}ms; elapsed=${elapsed}ms.`,
|
|
154
|
+
}],
|
|
155
|
+
extra: evidence,
|
|
156
|
+
};
|
|
157
|
+
} finally {
|
|
158
|
+
try {
|
|
159
|
+
const teardown = await destroyScratchNamespace(namespace);
|
|
160
|
+
evidence.namespaceDestroyed = teardown.destroyed;
|
|
161
|
+
} catch (err) {
|
|
162
|
+
evidence.teardownError = err.message;
|
|
163
|
+
process.stderr.write(`h2-cf-kv probe: teardown failed for namespace ${namespace && namespace.id}: ${err.message}; sweepOrphans will collect on next run.\n`);
|
|
164
|
+
}
|
|
89
165
|
}
|
|
90
|
-
|
|
91
|
-
const pass = observed === value;
|
|
92
|
-
return {
|
|
93
|
-
results: [{
|
|
94
|
-
anchorAcId: 'AC-5401-1',
|
|
95
|
-
verdict: pass ? 'pass' : 'fail',
|
|
96
|
-
detail: pass
|
|
97
|
-
? `write eventually appeared on the same-region read within elapsed=${elapsed}ms (CAP=${CAP_MS}ms)`
|
|
98
|
-
: `write did not appear within CAP=${CAP_MS}ms; elapsed=${elapsed}ms`,
|
|
99
|
-
}],
|
|
100
|
-
extra: { elapsedMs: elapsed, cap: CAP_MS },
|
|
101
|
-
};
|
|
102
166
|
}
|
|
@@ -46,7 +46,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
46
46
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
47
47
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
48
48
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
49
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
49
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
50
50
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
51
51
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
52
52
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -46,7 +46,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
46
46
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
47
47
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
48
48
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
49
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
49
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
50
50
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
51
51
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
52
52
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -46,7 +46,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
46
46
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
47
47
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
48
48
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
49
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
49
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
50
50
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
51
51
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
52
52
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -57,7 +57,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
57
57
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
58
58
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
59
59
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
60
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
60
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
61
61
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
62
62
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
63
63
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -46,7 +46,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
46
46
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
47
47
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
48
48
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
49
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
49
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
50
50
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
51
51
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
52
52
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -57,7 +57,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
57
57
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
58
58
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
59
59
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
60
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
60
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
61
61
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
62
62
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
63
63
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -46,7 +46,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
46
46
|
| application-account-settings | 25101-25899 | 26xx | shipped v1.0.0 | none |
|
|
47
47
|
| application-onboarding-tour | 26101-26899 | 27xx | shipped v1.0.0 | none |
|
|
48
48
|
| persistence-data-postgres | 27101-27899 | 28xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
49
|
-
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.
|
|
49
|
+
| object-storage-s3 | 28101-28899 | 29xx | shipped v1.1.0 | `objectStorageContract` |
|
|
50
50
|
| messaging-queue-cloudflare | 29101-29899 | 30xx | shipped v1.0.0 | `deliverySemantics` |
|
|
51
51
|
| jobs-background | 30101-30899 | 31xx | shipped v1.0.0 | `backgroundJobModel` |
|
|
52
52
|
| platform-cloudflare-kv | 31101-31899 | 32xx | shipped v1.0.0 | `keyValueStoreContract` |
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"registerCanary": [
|
|
3
3
|
{
|
|
4
|
-
"id": "rc-2026-09-
|
|
5
|
-
"createdAt": "2026-09-
|
|
6
|
-
"buildVersion": "0.
|
|
4
|
+
"id": "rc-2026-09-09-001",
|
|
5
|
+
"createdAt": "2026-09-09T16:11:26.221Z",
|
|
6
|
+
"buildVersion": "0.26.0-mockdriver",
|
|
7
7
|
"fixturePromptId": "canary-prompt-01",
|
|
8
8
|
"responseWordCount": 55,
|
|
9
9
|
"grades": {
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"shipDespiteFailReason": "mock canary driver used; no real subagent was dispatched. This record verifies canary infrastructure only, not the register itself."
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
"id": "rc-2026-09-
|
|
38
|
-
"createdAt": "2026-09-
|
|
39
|
-
"buildVersion": "0.
|
|
37
|
+
"id": "rc-2026-09-09-002",
|
|
38
|
+
"createdAt": "2026-09-09T16:11:26.222Z",
|
|
39
|
+
"buildVersion": "0.26.0-mockdriver",
|
|
40
40
|
"fixturePromptId": "canary-prompt-02",
|
|
41
41
|
"responseWordCount": 55,
|
|
42
42
|
"grades": {
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
"shipDespiteFailReason": "mock canary driver used; no real subagent was dispatched. This record verifies canary infrastructure only, not the register itself."
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
|
-
"id": "rc-2026-09-
|
|
71
|
-
"createdAt": "2026-09-
|
|
72
|
-
"buildVersion": "0.
|
|
70
|
+
"id": "rc-2026-09-09-003",
|
|
71
|
+
"createdAt": "2026-09-09T16:11:26.222Z",
|
|
72
|
+
"buildVersion": "0.26.0-mockdriver",
|
|
73
73
|
"fixturePromptId": "canary-prompt-03",
|
|
74
74
|
"responseWordCount": 55,
|
|
75
75
|
"grades": {
|
|
@@ -252,6 +252,73 @@ miss, feed unreachable) say nothing to the operator; freshness is a
|
|
|
252
252
|
convenience, not a gate. Never run the upgrade without the operator's
|
|
253
253
|
explicit go.
|
|
254
254
|
|
|
255
|
+
### RULE 15: On blueprint apply, dispose every AC.
|
|
256
|
+
|
|
257
|
+
A blueprint's acceptance criteria come in two shapes: `fixed`
|
|
258
|
+
(mechanism-invariant, true for every applying project) and `template`
|
|
259
|
+
(shape given, values project-specific). When you apply a blueprint
|
|
260
|
+
(`rcf define blueprint add ...`), walk every AC the blueprint
|
|
261
|
+
contributed. A `fixed` AC stands as authored: do not weaken or drop
|
|
262
|
+
one, and if one appears wrong for the project, that is a defect against
|
|
263
|
+
the blueprint, escalated on the blueprint's repo, not edited locally. A
|
|
264
|
+
`template` AC is a starting shape: actively dispose of it by accepting
|
|
265
|
+
as written, adjusting with the project's values, or dropping with a
|
|
266
|
+
stated reason recorded against the story. Silent inheritance of a
|
|
267
|
+
`template` AC is a defect. After you have disposed of every `template`
|
|
268
|
+
AC, re-sweep the applied AC set against the scenario-class prompt list
|
|
269
|
+
(credential missing, non-2xx, rate limit, timeout, partial write,
|
|
270
|
+
permission denied, malformed input, already exists, gone, concurrent
|
|
271
|
+
access, quota exhausted, dependency not ready, first-vs-repeat run,
|
|
272
|
+
idempotency, empty and maximal collections, boundary values) so the
|
|
273
|
+
story still covers the mechanism's documented failure paths. Any AC
|
|
274
|
+
that rests on a third-party platform fact must show its vendor
|
|
275
|
+
documentation URL and the ISO-8601 date the fact was verified; a
|
|
276
|
+
citation older than the operator's tolerance is re-verified before the
|
|
277
|
+
AC is treated as ground truth.
|
|
278
|
+
|
|
279
|
+
A blueprint's seed docs are a minimal representational seed set, not a
|
|
280
|
+
project spec. You have full scope of the project you are driving, and
|
|
281
|
+
you are the party best placed to integrate the seed with the project's
|
|
282
|
+
own chain. After you have disposed of every AC, walk the seed's REQs,
|
|
283
|
+
TACs and ADRs against the project chain (existing REQs, TACs, ADRs and
|
|
284
|
+
the PRD/TAD): resolve integration contradictions you can (parameter
|
|
285
|
+
substitution against a settled project value, naming reconciliation
|
|
286
|
+
against a settled project convention, composition-order reconciliation
|
|
287
|
+
against another applied blueprint), record the disposition in the
|
|
288
|
+
tree, and escalate to the operator only the ones that need a
|
|
289
|
+
product-shape decision or a two-way credential or account decision. An
|
|
290
|
+
internal contradiction (two of the seed's own layers disagreeing about
|
|
291
|
+
the same contract) is not yours to resolve; escalate it as a defect
|
|
292
|
+
against the blueprint's repo and refuse to derive downstream work from
|
|
293
|
+
the ambiguity.
|
|
294
|
+
|
|
295
|
+
When you escalate, pitch the question at the operator's declared
|
|
296
|
+
register. On your first exchange after `rcf init`, if
|
|
297
|
+
`rcf/.identity/profile.md` carries no `operatorRegister` value, ask one
|
|
298
|
+
question and record the answer to the profile: "One quick set-up
|
|
299
|
+
question. When we need to escalate a decision to you, do you want it
|
|
300
|
+
framed as a product-owner question (plain English, outcome-shaped) or
|
|
301
|
+
an engineer question (with the AC id, the seed's assertion, the
|
|
302
|
+
project's convention)? Answer 'product owner', 'engineer', or 'skip'
|
|
303
|
+
and I will hold the default." A `skip` answer writes `unstated`, which
|
|
304
|
+
reads as `productOwner` on the first escalation; if the operator
|
|
305
|
+
answers in method vocabulary, silently promote to `engineer` for the
|
|
306
|
+
rest of the session and offer to persist the change to the profile.
|
|
307
|
+
|
|
308
|
+
### RULE 16: Verify with positive evidence.
|
|
309
|
+
|
|
310
|
+
Every "verified" claim (in the Test and Finalise stages, in the PR body's
|
|
311
|
+
verification section, in any status update) names the runtime it was
|
|
312
|
+
checked against AND cites the positive evidence of the property (a
|
|
313
|
+
request id, a response body excerpt, a created-then-deleted resource id
|
|
314
|
+
in an inventory diff, a real deploy record). "The check did not show the
|
|
315
|
+
bad signature" is not evidence; "the request returned id 8f7a-... and
|
|
316
|
+
the response body carried the created resource id 12345" is. Where the
|
|
317
|
+
account credentials are not present, record the skip honestly (name the
|
|
318
|
+
env var that was unset), never fabricate a pass on the strength of
|
|
319
|
+
absence. This rule mirrors RULE 15's active-disposition posture: an
|
|
320
|
+
absent check is a skip you name, an actual check is a claim you cite.
|
|
321
|
+
|
|
255
322
|
### Session start
|
|
256
323
|
|
|
257
324
|
Run `rcf define validate`. A broken tree is fixed or reported before anything
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d35bd2dfec14db5567c2bcfa2934ce58ac5d7c23fd24e47776501660e06063eb
|