Repository navigation
Conversation
Every /api/logistics/* route answered from hard-coded fixtures: providers, shipments, space bookings, installation orders, a timeline, counts over them, and a quote priced by a made-up formula under fixture providers' names. The installation-step PATCH edited a fixture in memory. One onRequest hook, encapsulated to the plugin, now answers 501 not_available before the body is parsed unless PCC_DEMO_ROUTES=true, so nothing is read, priced or changed. The refusal says what is not recorded and, for shipments, points at the real carrier route. In demo mode the old answers come back marked mock/demo, with the x-pcc-demo header. Board N34, the server side of PX-3. agent: implementer-alpha-n34 (pcc-readmodels c255d7dc)
…cores /api/spaces and /api/spaces/:id answered from two hard-coded spaces (names, street addresses, operator wallets, power, slots, ratings), and POST /api/spaces/match scored them with Math.random(): a 70-95 matchScore with no basis, different on every call. One onRequest hook, encapsulated to the plugin, now answers 501 not_available before the body is parsed unless PCC_DEMO_ROUTES=true. In demo mode the old answers come back marked mock/demo, with the x-pcc-demo header. Board N34, the server side of PX-3. agent: implementer-alpha-n34 (pcc-readmodels c255d7dc)
… data GET /api/agents/conversations and /:convId answered from two hard-coded conversations (topics, participant agents, message counts, timestamps) and read no agent bus. One onRequest hook, encapsulated to the plugin, now answers 501 not_available unless PCC_DEMO_ROUTES=true, and points at GET /api/agents/live/conversations, which reads the gateway's agent bus. In demo mode the old answers come back marked mock/demo, with the x-pcc-demo header. The heartbeat routes under /api/agents/* are untouched. routes.test.ts still asserts the fixture answers without the flag (4 tests); it is outside this change's file scope and is left for the lane owner. Board N34, the server side of PX-3. agent: implementer-alpha-n34 (pcc-readmodels c255d7dc)
Board N34 (server side of PX-3), steward rulings #2498 and #2639: a production surface never substitutes plausible mock data for real data. Every /api/marketplace route but one answered from invented data: equipment classes with made-up machine counts, utilization and job values, a sine-wave price history and demand curve, supplier listings and orders. Its writes pushed into those fixtures and wrote a telemetry event and an audit entry for a listing or order that exists nowhere. - SERVED-MOCK (11): GET /classes, /classes/:id, /demand-supply, /listings, /listings/:id, /orders, /orders/:id, and POST /listings, PUT /listings/:id, DELETE /listings/:id, POST /orders. Unless PCC_DEMO_ROUTES=true they answer 501 not_available before any fixture is read or changed (and before input validation, the telemetry event and the audit entry, so an unknown id cannot reveal which fixture ids exist). `see` names the real routes where they exist: capability types/templates/by-type, the kernel marketplace, jobs, kernels. In demo the old answers stay, each marked mock/demo (errors included). - MIXED: GET /categories keeps the spec's category taxonomy; the per-category listing counts came from the fixture, so outside demo they are null and named in unavailable: ["categories[].count"]. - REAL: POST /roi is a calculator over the caller's own inputs; unchanged. Tests: n34-marketplace-no-fabrication.test.ts, 30 cases (11 routes x refused with no fixture value and x demo-marked with the old shape; refusal before validation; unknown ids; refused writes change no fixture and write no audit entry; the categories split; /roi identical with the flag on and off). Mutation-checked 4/4 red: dropping the GET /listings, POST /listings or POST /orders guard, or feeding fixture counts into the non-demo categories answer. agent: implementer-bravo-n34 (pcc-readmodels c255d7dc)
…real Board N34 (server side of PX-3), steward rulings #2498 and #2639: a production surface never substitutes plausible mock data for real data. The ERC-8004 registry reads identity and reputation from chain, but the attestation reads had no source: one invented attestation (validator 0xcccc..., claim hash 0xaaa111..., "FDM printing - PLA/PETG ...") was served as registry content, in two lists, an entity's detail and the summary counts. - SERVED-MOCK: GET /api/registry/attestations and /attestations/:id answer 501 not_available unless PCC_DEMO_ROUTES=true (before the id lookup, so an unknown id reveals nothing). `see` names the registry reads that are real: /api/registry/entities/:entityId and /api/registry/reputation/:entityId. In demo the fixture answers stay, marked mock/demo (the 404 included). - MIXED: GET /entities/:entityId keeps the chain-read entity and reputation; outside demo attestations is null and named in unavailable: ["attestations"]. GET /summary keeps the DB entity counts; outside demo totalAttestations and activeAttestations are null, named in `unavailable`, and source.attestations is null (it said "mock"). In demo both keep the fixture values and are marked mock/demo. The 400 and 404 answers come from real reads and stay unmarked. - REAL, unchanged: GET /entities, /reputation/:entityId, /reputation/leaderboard. Tests: n34-registry-no-fabrication.test.ts, 17 cases on a mocked chain (no network): the attestation routes refused with no fixture value and demo-marked; the entity detail and summary split with a seeded minted agent; the real routes identical with the flag off and on, never marked. routes.test.ts (summary/entities) still green. Mutation-checked 4/4 red: dropping either attestation guard, or serving the fixture attestations in the non-demo entity detail or summary. agent: implementer-bravo-n34 (pcc-readmodels c255d7dc)
…nt record The 501 for GET /api/logistics/timeline listed no real route, although the gateway does record logistics events: a carrier shipment's tracking events, owner-scoped, at GET /api/carrier/shipments/:jobId. The shipment refusals already pointed there; the timeline (four of its five fixture events were shipment tracking) now does too. Summary, quote, providers, bookings and installations keep an empty list: no real route holds their data. Board N34, the server side of PX-3. agent: implementer-alpha-n34 (pcc-readmodels c255d7dc)
…refuses The spaces test showed the refusal body carries no matchScore. It did not show the random score is never drawn. A Math.random spy now shows no draw on the 501 path, and a draw on the demo path, so the spy is known to see one. Board N34, the server side of PX-3. agent: implementer-alpha-n34 (pcc-readmodels c255d7dc)
… demo Board N34 (server side of PX-3), steward rulings #2498 and #2639: a production surface never substitutes plausible mock data for real data. Every route in rewards.ts answered from invented data: two reward epochs with kernel scores and reward amounts, claims (one with a made-up transaction hash), three soulbound capability certificates, and a "broker-agent" treasury holding 50,000 USDC and 10.5 ETH. POST /api/rewards/claims answered created:true and POST /api/certificates/mint answered minted:true; neither recorded or minted anything. - SERVED-MOCK, all 9 routes: GET /api/rewards/epochs, /api/rewards/epochs/:epochId, /api/rewards/kernels/:kernelId, /api/rewards/claims/:claimId, /api/certificates, /api/certificates/:certId and /api/treasury/summary, and POST /api/rewards/claims and /api/certificates/mint. - Because every route is SERVED-MOCK, the plugin is gated by one encapsulated onRequest hook: unless PCC_DEMO_ROUTES=true each route answers 501 not_available with its own "... not recorded on this gateway" message and see: [] (no real source exists here). The hook runs after the root API-key gate, so a caller without a key still gets 401, and before the body is parsed, so a refused write creates, mints and records nothing, and an unknown id reveals nothing. - In demo the old answers stay, each marked mock/demo (the 400 and 404 answers included). Tests: n34-rewards-no-fabrication.test.ts, 25 cases: the 9 routes refused with their own message and no fixture value, and in demo the old status and shape, marked; unknown ids and filters refused; malformed JSON on a write is 501, not 400; HEAD refused; demo 404/400 marked; the hook does not leak to the parent's routes or to sibling plugins (marketplace's real POST /roi, a probe plugin) and an unmatched path stays 404; with apiGate registered first, no key gets 401 and a key gets the refusal. Mutation-checked 7/7 red: gate removed, writes ungated, reads ungated, gate moved to preHandler, gate leaked via skip-override, demo treasury unmarked, and rewards registered before apiGate (caught by the 401-first case). agent: implementer-bravo-n34 (pcc-readmodels c255d7dc)
…o fix/n34-no-fabricated-routes Group a of board row N34 (steward #2498; carrier #2922 retires logistics). All 17 handlers in logistics.ts, spaces.ts and agents.ts serve fixtures or random scores, so each plugin answers 501 not_available unless PCC_DEMO_ROUTES, and demo answers are marked. Implemented by implementer-alpha-n34 (5 commits); integrated here. agent: pcc-readmodels (c255d7dc)
…) into fix/n34-no-fabricated-routes Group b of board row N34. 29 handlers classified: SERVED-MOCK routes answer 501 not_available unless PCC_DEMO_ROUTES; mixed registry reads null their mock fields and list them in unavailable; real reads are unchanged. Implemented by implementer-bravo-n34 (3 commits); internal review APPROVE_WITH_NITS, nits folded in the next commits. agent: pcc-readmodels (c255d7dc)
#326's scoped keys routes.test.ts expected 200 fixture conversations from /api/agents/conversations; they now expect the honest 501 not_available (with the pointer to the recorded conversations), and that a fixture id and an unknown id answer alike. The rewards gate test mints its probe key with explicit scopes ([]), which #326 requires. agent: pcc-readmodels (c255d7dc)
Every 5xx got a report_hint telling agents to report a bug (reviewer-bravo-n34). A 501 not_available (a route with no real source, N34) and a 503 not_configured (an unconfigured provider, N48) are documented, intentional answers, so they no longer carry it. Any other 5xx still does. agent: pcc-readmodels (c255d7dc)
Group b's see entries were bare paths while group a's named the method. They are now all 'GET /api/...'. The registry attestation refusal pointed at the entity and reputation reads, which hold no attestations, so its see is empty (reviewer-bravo-n34 nit). agent: pcc-readmodels (c255d7dc)
…nters bonjour-service is not a dependency, so on every deployed gateway the mDNS import fails, and /api/discover/scan answered two example printers (Canon, HP) as if found. /api/discover/onboard then REGISTERED one of them in the CSD registry. Found by the N34 ratchet inventory; not in the census. - Without mDNS: 501 not_available (discover on the operator's machine, pcc-node). - With PCC_DEMO_ROUTES: the examples, marked mock/demo; demo onboarding returns the would-be device and CSD with registered:false and writes nothing. - discover.test.ts updated from the mock expectations; 13 tests. agent: pcc-readmodels (c255d7dc)
Steward #2498. For every file under src/routes it counts mock data declarations (mock*/MOCK_*/fake*/FAKE_* bound to an array, object, Map or Set) and value-producing Math.random() calls (not the toString(36) id idiom). A file may have them only as the allowlist says, and each entry names an owner and why it stays for now. Shrink-only: a count above its allowance fails, and so does an allowance above the count. The detector has its own tests. agent: pcc-readmodels (c255d7dc)
Every /api/orchestrator/{graphs,samples,claims,workflows} route answered
from one made-up lab kernel (kernel-biolab-01): a five-instrument transfer
graph, two samples with a movement history, a running workflow and its
resource claims. Nothing was read from the kernels, devices or jobs the
gateway records. GET /graphs/:kernelId answered {error:"not_found"} with
HTTP 200 for every real kernel, and POST /workflows said accepted:true and
recorded nothing.
All eight routes are SERVED-MOCK, so one encapsulated onRequest hook gates
the plugin: unless PCC_DEMO_ROUTES=true (never under NODE_ENV=production),
each answers 501 not_available before the body is parsed, with pointers to
the real routes where they exist (kernels and their devices, jobs, job
submission). In demo the old answers stay, each marked mock:true, demo:true
and x-pcc-demo: true. The hook does not reach the other /api/orchestrator/*
plugins (template directory, data-product sessions); the new test proves it,
with a skip-override control that shows the check can fail.
Board N34, the server side of PX-3.
agent: implementer-charlie-n34 (pcc-readmodels c255d7dc)
Every /api/protocols*, /api/protocol-runs*, /api/automation-status* and /api/transfer-agents route answered from fixtures that nothing records: a two-template "library" with invented authors and usage (runCount 7, rating 4.6), a "running" protocol run on a made-up kernel with evidence hashes and robot transfer episodes, a fork, automation statuses and transfer agents. The writes (create, update, publish, fork, start a run, pause, resume, cancel, record an episode, advance a level) answered created, updated, started or recorded and stored nothing. /validate checked templates against a hard-coded capability list, not the kernel's. All 21 routes are SERVED-MOCK, so one encapsulated onRequest hook gates the plugin: unless PCC_DEMO_ROUTES=true (never under NODE_ENV=production), each answers 501 not_available before the body is parsed, with its own message and pointers to the real routes where they exist (jobs and job submission for run state, a kernel's capabilities for /validate). In demo the old answers stay, each marked mock:true, demo:true and x-pcc-demo: true. The new test proves the hook reaches no other plugin (skip-override control included) and that every registered route has its own refusal. Board N34, the server side of PX-3. agent: implementer-charlie-n34 (pcc-readmodels c255d7dc)
…de demo POST /api/swf/epochs/:epochId/distribute scored every active participant with Math.random() (job count, reputation, uptime, votes) and then DISTRIBUTED the epoch on those scores: pending dividend claims with real-looking amounts, and the epoch marked completed. Nothing records a participant's per-epoch contribution, so the shares were set by chance. Unless PCC_DEMO_ROUTES=true (never under NODE_ENV=production), the route now answers 501 not_available BEFORE anything is read, scored or written, says why (the shares would come from random numbers) and points at GET /api/swf/epochs/:epochId; the epoch stays exactly as it was. In demo the old distribution runs and its answers (200, and the 409 conflict) are marked mock:true, demo:true. It is the only route in swf.ts that draws random values or reads a fixture; the rest read and write the SWF service's own records and are unchanged. The new test proves that with the flag on and off. Board N34, the server side of PX-3. agent: implementer-charlie-n34 (pcc-readmodels c255d7dc)
…ly at live routes Two checks the per-route tables could not make on their own: - every route the orchestrator (8) and protocols (21) plugins register is refused with its own message, so a route added later, or a refusal key that no longer matches its route pattern, cannot fall back unnoticed; - every `see` pointer in the refusals (orchestrator, protocols, swf) names a route the gateway actually registers (Fastify hasRoute on the kernels, jobs, job-submit, capabilities and swf plugins), so a refusal never sends a caller to a dead path. Board N34, the server side of PX-3. agent: implementer-charlie-n34 (pcc-readmodels c255d7dc)
… N34 integration wip/n34-c @6ba90ef9 by implementer-charlie-n34, reviewed APPROVE_WITH_NITS by reviewer-n34-c: 104 new tests, full gateway suite 3566/6/0 twice on its base, 15/15 reviewer mutants killed. Outside demo, all 29 orchestrator and protocol routes answer 501 not_available before the body is parsed. SWF epoch distribution (random contribution scores) is refused per route before any read, score or write; the other SWF routes are unchanged. agent: pcc-readmodels (c255d7dc)
…easons) reviewer-n34-c: the SWF distribute refusal said per-epoch contribution DATA (jobs, reputation, activity, votes) is not recorded, but jobs and votes are recorded; what is missing is a per-participant, per-epoch contribution score. The message now says exactly that. The ratchet's orchestrator, protocols and swf entries now state the demo-only reason, and swf names economics as the owner (#360 replaces the handler). agent: pcc-readmodels (c255d7dc)
reviewer-n34-c: services.ts seeded a demo HPLC batch (Sample Alpha, Beta and Gamma, jobs job-010..012, made-up 0x wallets, a fake sha256 result) into every gateway's batch tracker at import, so GET /api/batches* served it as live data on production. It is now seeded only when PCC_DEMO_ROUTES=true (never under NODE_ENV=production), and its runConfig says demo: true. The batch routes are unchanged: they serve what the tracker holds. Tests: n34-batches-no-fabrication (3). Removing the gate fails both negative tests. agent: pcc-readmodels (c255d7dc)
reviewer-n34-a and reviewer-n34-c: /agent-context-pack(.json) still described the routes that now answer 501 not_available as live endpoints, so an agent following the pack walked into refusals. The 44 such rows (marketplace classes and demand-supply, discover scan and onboard, protocols and protocol runs, rewards claims, certificate mint, treasury summary, spaces, logistics, orchestrator, agent conversations) move to one list, "Not available on this gateway (demo only)" in the markdown and `demoOnlyEndpoints` in the JSON, rendered from a single constant. Sections left empty are dropped. context-pack-availability.test.ts asks the N34 route families which routes refuse with PCC_DEMO_ROUTES unset, then checks that no available row refuses, every demo-only row does, and both pack forms agree. It fails on each of: a refused row put back in a table, a live route listed as demo-only, a refused row put back in the JSON groups, and the JSON demo list dropped (4/4). agent: pcc-readmodels (c255d7dc)
The API reference described rewards, logistics, spaces, marketplace listings and orders, discover scan and onboard, protocols and orchestrator as live. Each row now says it answers 501 not_available outside demo mode, names the live routes that remain (marketplace roi and categories, discover generate-csd) and the live pointer where one exists. SWF epoch distribution and the demo-only HPLC batch are noted. The MCP tools that call only refused routes (pcc_list_protocols, pcc_depin_stats, pcc_discover_scan, pcc_discover_onboard) are marked. agent: pcc-readmodels (c255d7dc)
…on-numbers
reviewer-n34-b: POST /api/marketplace/roi silently filled missing inputs
with example values, so a body of {} returned a plausible 25-month
projection that read as the caller's own. The answer now carries
`inputs` (the values used), `defaulted` (which ones were examples) and
`assumptions` (30 days a month, 70% bookable, 3 months up front, a
24-month horizon). A numeric string is still used, as before. An input
that is not a number answers 400 invalid_input instead of a NaN
projection. The dashboard computes its ROI client-side and does not call
this route.
agent: pcc-readmodels (c255d7dc)
… demo path Economics #3315 and the steward's SWF ruling (operator item 69): the fund stays a design artifact and its money routes stay 501; distribute is on N46's money-floor list. So POST /api/swf/epochs/:epochId/distribute refuses whatever PCC_DEMO_ROUTES says, before any read, score or write (the random-score demo distribution is removed, and with it swf.ts's last Math.random calls and its ratchet entry). The refusal keeps the reviewed wording and METHOD-path `see` pointers, now also pointing at GET /api/swf/accruals. #360 makes the same handler an unconditional 501, so whichever lands second resolves a wording-only conflict. agent: pcc-readmodels (c255d7dc)
LamaSu
added a commit
that referenced
this pull request
Sep 30, 2026
…bels simulation totals Cross-family review EC2 (astra, SHIP-WITH-FIXES at 4e79258). Each finding was reproduced at 4e79258 with a test that fails there, then fixed: - F1 (HIGH): accepting terms, activating an equity position, recording revenue and distributing an epoch now answer 501 not_available before anything is read or written. At 4e79258 they answered [200, 409, 409, 409] and the portfolio showed deployed capital and earned revenue that no transfer backed. The distribute handler is #421's hunk verbatim. - F2 (MEDIUM): lastDistributionAt is always null. A completed epoch is a simulated split whose endTime is its scheduled deadline, so the summary reported a future time as a distribution. - F3 (MEDIUM): the summary says basis "simulation" and lists the three money totals as unavailable, so a zero reads as an empty simulation ledger, not a measured fund. - F5 (MEDIUM): the ratchet now catches any spelling. swfAccrue may be named only at its definition (no import, alias or re-export), and .accrue( may appear only in routes/swf.ts. Behavioural tests show settlement release leaves the SWF ledger unchanged. Two comments that named the helper are reworded. F4 (the dashboard's MOCK_SUMMARY page) is outside this diff; #394 retires that page. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…code alone
Reviewer finding F-C (r1a): buildReportHint suppressed the report_hint for ANY
5xx carrying error "not_available" or "not_configured", regardless of status
code. An accidental `500 not_available` or `599 not_configured` — a real bug —
was silently treated as the designed N34/N48 refusal (501/503) and lost its
report_hint. Existing tests covered the intended pairs and lookalike error
strings, but not mismatched statuses.
Reproduced in report-hint.test.ts: buildReportHint({url:"/api/x", method:"GET",
statusCode:500, errorCode:"not_available"}) returned null at d3647b8. Added
assertions for all four mismatches (500/not_available, 599/not_configured,
501/not_configured, 503/not_available) — all returned null before the fix.
Fix: replaced the code-only INTENTIONAL_REFUSAL_CODES.has() check with an
INTENTIONAL_REFUSAL_STATUS map pairing each code to its one designed status
(not_available -> 501, not_configured -> 503); suppression now requires the
code AND the status to match. INTENTIONAL_REFUSAL_CODES is kept exported
(unused elsewhere, but preserves the public surface) since it wasn't the
gate itself, just documentation of which codes are involved.
agent: implementer-alpha for pcc-readmodels (c255d7dc)
…time state Reviewer finding F-D (r1b): services.ts seeds one demo HPLC batch into the shared BatchTracker singleton at import, only when PCC_DEMO_ROUTES was on. The batch routes then served the tracker's contents unconditionally, so a process that started in demo mode and later had PCC_DEMO_ROUTES turned off kept serving the seeded fixture — the on-to-off transition was never rechecked, only the state at seed time. Reproduced in n34-batches-no-fabrication.test.ts: import batches.js with demo on (seeds the fixture), confirm it's present, delete PCC_DEMO_ROUTES in the SAME process (no re-import), then GET /api/batches and /api/batches/by-job/job-010 — at d3647b8 both still returned the fixture (HPLC batch, Sample Alpha/Beta/Gamma, job-010..012). Fix: batches.ts now reads config/demo-routes.ts's isDemoRoutesOn() on every read (GET /api/batches, /api/batches/:batchId, /api/batches/by-job/:jobId) and filters out any batch whose runConfig.demo === true when demo mode is currently off (BatchManifest.runConfig is always present per @pcc/spec, so no optional-chaining edge case). A batch a caller actually creates has no demo flag on its runConfig and stays visible regardless of demo mode — added a test asserting this explicitly, so the fix cannot be "hide everything". Batch writes (POST .../slots) are untouched, per the finding: they mutate real in-memory state by design. agent: implementer-alpha for pcc-readmodels (c255d7dc)
Reviewer finding F-B (r1a): countFabrication() only recognized bindings whose
NAME started with mock/MOCK_/fake/FAKE_ and whose INITIALIZER started with
`[`, `{`, `new Map(`, or `new Set(`. Reproduced the reviewer's three cheapest
cases directly: countFabrication("const demoRows = [{id: 1}]"),
countFabrication("const mockRows = Array.from({length: 2}, (_, i) => i)"),
and countFabrication('import rows from "./fixture.json"') all returned
{mockData: 0, random: 0} at d3647b8 — a "demo"-named binding, a
recognized-name binding with an unrecognized initializer shape (Array.from),
and a JSON import all evaded it, as did any allowlisted fixture swapped for
a differently-named one of the same count (the allowlist pinned by count
only).
Fix (n34-routes-fabrication-ratchet.test.ts):
- Binding-name matching now recognizes demo/mock/fake/sample/seed/fixture as
a prefix OR suffix, with a case-sensitive camelCase/ALL_CAPS boundary check
(hasFixturePrefix/hasFixtureSuffix) — deliberately NOT a bare case-insensitive
substring match, which produced real false positives caught while iterating:
"isDemoBatch" (a predicate, "Demo" mid-word, no prefix/suffix boundary),
"sampler"/"currentSampler" (extra trailing letters after "Sample"),
"downsamplePoints" ("sample" embedded with no case-transition boundary).
- Initializer matching now accepts ANY shape except a string/template literal
(STRING_INITIALIZER_RE) — catches Array.from(...), Object.assign(...),
structuredClone(...) — while still excluding derived-value cases the
existing tests require (MOCK_NOTE = "a warning string", mockAddress =
`0x${hex}`).
- Declarations are scoped to top-level (module-scope) only, via the
ENCLOSING LINE's leading character rather than anchoring the regex match
itself to `^` (isTopLevel) — this is what actually excludes handler-local
variables derived from an already-counted fixture (`let samples =
[...mockSamples]`, `const sample = mockSamples.find(...)` inside a route
handler) without also rejecting a second same-line top-level declaration
(`const url = "..."; const mockX = [];`, an existing test case).
- Added a JSON_IMPORT_RE pass: `import x from "*.json"` counts regardless of
the imported binding's name.
- Allowance gained a `names: string[]` field; a new test asserts the
ALLOWLIST's name SET matches each file's actual fixture-binding names
exactly (not just the count), so swapping one allowed fixture for a
new one of the same count now fails.
Verified against every real route file (not just the unit cases): re-running
the strengthened detector across src/routes/ produces IDENTICAL counts to
the existing ALLOWLIST for every file except context-pack.ts, which newly
trips on its own DEMO_ONLY_ENDPOINTS array (a real "demo"-prefixed,
array-shaped top-level binding) — pinned with an honest reason (it's the
context pack's own documentation of real refusals, not served business
data, per F-E in this same PR).
Kept countFabrication()'s {mockData, random} return shape unchanged so the
existing "detector" unit tests didn't need rewriting; added fixtureNames()
as a separate export for the allowlist's by-name comparison.
agent: implementer-alpha for pcc-readmodels (c255d7dc)
…uest)
Reviewer finding F-A (r1a): the marketplace gates in packages/gateway/src/
routes/marketplace.ts:400-405,481-486 (and their siblings) lived INSIDE each
route handler as `if (!isDemoRoutesOn())`. Fastify parses the JSON body
before invoking a handler, so a malformed payload on a gated write hit the
parser's own 400 before the intended unconditional 501 was ever reached.
Reproduced in n34-marketplace-no-fabrication.test.ts: POST
/api/marketplace/listings (and /orders, and PUT .../listings/:id) with
Content-Type: application/json and payload "{not json" returned 400 "Bad
Request" (Fastify's own parser error) at d3647b8, not the 501 not_available
every other malformed/empty request to these routes gets.
Fix: added an encapsulated onRequest hook to marketplaceRoutes (the
rewards.ts/logistics.ts pattern), keyed by "METHOD /route" via a
REFUSED_ROUTE_KEYS table carrying each route's exact existing message and
`see` array. The hook runs before preParsing, so a listed route now refuses
before ANY input is read — same guarantee logistics/orchestrator/protocols/
spaces/agents/rewards already have. GET /api/marketplace/categories and
POST /api/marketplace/roi are intentionally NOT in the table (mixed/live
routes per the existing MIXED and REAL test suites) — the hook no-ops for
them and they reach their handlers unchanged. Existing per-handler
`if (!isDemoRoutesOn())` checks are left in place as (now unreachable when
demo is off) dead code — cheapest correct diff; demo-on behavior is
untouched since the hook returns early when isDemoRoutesOn() is true.
Verified: full n34-marketplace-no-fabrication.test.ts (33/33), the N34
ratchet (marketplace.ts's fixture count unchanged at 4 — the new
REFUSED_ROUTE_KEYS binding doesn't match its demo/mock/fake/sample/seed/
fixture name patterns), context-pack-availability.test.ts and
n34-rewards-no-fabrication.test.ts (both also register marketplaceRoutes)
all still pass. tsc --noEmit clean.
agent: implementer-alpha for pcc-readmodels (c255d7dc)
…refuses Reviewer finding F-E (r1b): DEMO_ONLY_ENDPOINTS omitted at least ten refused routes named in the finding (logistics installation-step PATCH; orchestrator sample/workflow collection GETs; protocol template/run collections, automation-status routes, transfer-agent GET) plus more the finding didn't enumerate. The existing context-pack-availability.test.ts only checked the forward direction (documented rows really refuse), never the reverse (every refused route is documented). Reproduced: added a test that derives the refused set from the SAME route registration context-pack-availability.test.ts's own beforeAll already builds (agents/discover/logistics/marketplace/registry/rewards/spaces/ orchestrator/protocols/swf, demo off, every route probed), and asserts refused subset-of documented. At d3647b8 this failed with exactly 27 missing routes. Fix: completed DEMO_ONLY_ENDPOINTS in context-pack.ts with all 27 — 8 in marketplace (listings/orders GET+POST+PUT+DELETE, left ungated on the list until F-A's onRequest fix in this same PR), 6 in protocols (GET /api/protocols and /api/protocol-runs collections, automation-status GET/POST x2, GET /api/transfer-agents), 2 in orchestrator (samples and workflows collection GETs), 2 in registry (GET /api/registry/attestations and /:attestationId — a family with no prior representation in this list at all), 1 in logistics (the installation-step PATCH), and 1 in rewards + certificates (GET epochs, GET epochs/:id, GET kernels/:id, GET claims/:id, GET certificates, GET certificates/:id — 6 routes, all already-live siblings of the 3 rewards routes that WERE documented). No existing documented entry was removed. One entry needs the lane's attention: POST /api/swf/epochs/:epochId/distribute also surfaced as refused-but-undocumented (it answers 501 not_available unconditionally, no demo path, per swf.ts's own comment "boards N34 and N46 (money floor)"). Per this task's scope split, packages/gateway/src/ routes/swf.ts is lane-owned and I did not touch it — I only added a documentation line for its EXISTING behavior to context-pack.ts (not an excluded file). If the lane's own SWF work changes that route's refusal shape, this one line in DEMO_ONLY_ENDPOINTS may need to move with it. Verified: context-pack-availability.test.ts (5/5, including the new reverse-direction test), the N34 ratchet (context-pack.ts's DEMO_ONLY_ENDPOINTS binding count stays 1 regardless of array length — consistent with the F-B allowlist entry added for it in this PR). tsc --noEmit clean. agent: implementer-alpha for pcc-readmodels (c255d7dc)
…teral (N34) The #373 merge brings fiat-ramp.ts's DEMO_WALLET_REF_RE, the pattern a demo walletRef must match. The ratchet's stronger detector (8162ce9) counted it as fixture data because its name starts with DEMO_, so the merged branch failed the ratchet. An initializer that is exactly one regular-expression literal is a pattern that checks input. It is not data, like the string literals the detector already exempts. A regex that is called (/x/.exec(rows), /,/[Symbol.split](text)) still counts. The tests pin both cases. agent: pcc-readmodels (c255d7dc)
…s nothing into it (N34) Astra round 1 on #421 (r1a): the HIGH, and the SWF half of the MEDIUM. Reproduced first: 7 of the 8 tests in n34-swf-demo-only.test.ts fail at 4a28d7a, whose SWF and settlement code is d3647b8's. - With demo off, GET /api/swf/summary served an epoch seeded at import, a "last distribution" of now, and a Base USDC balance that no chain read produced. - A malformed JSON body to /api/swf/participants or to the distribute route got the parser's 400, not the refusal. - Demo answers carried no mock or demo marker. - A released milestone accrued an invented 1,000 USDC into the SWF, through both the release route and the settlement facade: 2 accruals where there should be 0. Now one encapsulated onRequest gate covers every /api/swf route: - Epoch distribution answers 501 not_available in every mode, before the body is parsed (operator item 69). Its see list is empty, because the routes it named are refused too. - Outside demo mode, and always under NODE_ENV=production, every other route answers 501 not_available before its body is parsed. Nothing is read or written. - In demo mode the simulation answers, with the x-pcc-demo header and mock: true, demo: true on object bodies (the orchestrator's pattern). Its first epoch opens on the first demo request, not at import. Also: - settlement.ts and settlement.facade.ts no longer accrue into the SWF. The release result carries no amount to accrue. swfAccrue and swfAutoRegister had no other callers and are removed. - The agent context pack lists all 27 SWF routes as demo-only and none as available. CLAUDE.md says the same. n34-swf-no-fabrication.test.ts keeps its distribution refusals, now read through the service. Its "real routes in both modes" section becomes demo-mode checks: the same answers, now marked, and an allocation still invents no budget. Mutation: removing the demo gate, the distribute pre-parse refusal or the demo marking each fails the SWF tests. agent: pcc-readmodels (c255d7dc)
…o a call on the next line still counts (N34) Found while packing afeda4b for review: the exemption accepted a regex literal followed by a newline. JavaScript continues an expression across that newline when the next line starts with .exec(...), [, (, an operator and so on. So `const mockMatch = /x/` followed by ` .exec(rows)` on the next line was exempt although the regex is called. The literal must now end its statement: spaces or tabs, then `;`, the end, or a newline whose next token does not continue the expression (. [ ( ` ? or a binary operator, in, instanceof). New tests pin three continued forms as counted, and a literal followed by a comment and a new statement as exempt. With the old pattern, the new test fails. The header comment now describes the detector as it is: - names with a demo, mock, fake, sample, seed or fixture prefix or suffix, whatever the initializer, except a bare string or regex; - JSON imports; - the allowlist's per-name pins; - what the ratchet cannot see (inline literals in a handler, an imported mock service) and which tests cover those. agent: pcc-readmodels (c255d7dc)
Found while packing d957015 for review. The marketplace onRequest gate keyed its refusal table on req.method, so HEAD on a gated GET missed the table. It was refused only because each GET handler still checks isDemoRoutesOn. Removing those checks, which d957015's message calls dead, would have opened HEAD. The gate now maps HEAD to GET, as the orchestrator and protocols gates do. A test asserts that HEAD on every gated marketplace GET answers 501. agent: pcc-readmodels (c255d7dc)
…outside demo mode (N34) Found while packing #421. CLAUDE.md marked these tools, but the descriptions agents actually read still advertised them as live: the MCP server's tool descriptions, the dashboard's .well-known/mcp/server-card.json and the CLI help. The tools are pcc_list_protocols, pcc_depin_stats, pcc_discover_scan, pcc_discover_onboard, pcc_swf_summary, pcc_swf_participant_dashboard and pcc_swf_list_proposals. Each now adds: outside demo mode (PCC_DEMO_ROUTES) the gateway answers 501 not_available. The tools were found by matching each tool's gateway path against the context pack's demoOnlyEndpoints. In the server card, the seven description lines changed and nothing else. agent: pcc-readmodels (c255d7dc)
…ent, so "a,b".split(",") counts (N34)
Found while packing 7e60f95, the same class as the regex hole closed
in 32f663c. The string exemption tested only the first character, so
any initializer that starts with a quote was exempt, a call or a join
included. The packer's grid found 998 of 1,484 TypeScript-valid inputs
that TypeScript reads as a larger expression.
Both exemptions now share one statement-end rule (STATEMENT_END). The
string exemption accepts string or template literals only, joined by
`+` (a note split over lines, like DEMO_ONLY_NOTE and
MOCK_WALLET_NOTE). Anything else joined in, called or continued still
counts. New tests cover a split call, a continued template, a literal
plus a variable, a literal plus a join, and a multi-line note. With
the old first-character rule, the new test fails.
agent: pcc-readmodels (c255d7dc)
…he docs carry the SWF and tool notes (N34) Found while packing 7e60f95: - 7e60f95's sentence was wider than the discover routes. They answer 501 only where the gateway has no local network discovery (mDNS), which means any hosted gateway. With discovery they return real devices. The two discover tools now say exactly that, in the MCP server, the server card and the CLI help, and in CLAUDE.md's and the integration guide's rows. pcc_discover_scan no longer claims it "falls back to mock devices in dev mode": the examples are returned only in demo mode, marked. - docs/AGENT_INTEGRATION.md's /api/swf/* row is now the same as CLAUDE.md's: 501 outside demo mode, distribution 501 in every mode. Its three pcc_swf_* tool rows carry the 501 note. - packages/mcp-server/README.md lists the seven tools with the same notes. agent: pcc-readmodels (c255d7dc)
LamaSu
added a commit
that referenced
this pull request
Oct 3, 2026
#360 was SHIP at c73d708 (EC1c) with full CI green, but master moved on 9/30–10/01 (#424, #459 N80, #436, #338, #477, #483 and others), and three files conflicted. Resolutions: - packages/spec/src/index.ts: both sides added exports at the same place. Both are kept: #360's `economics` namespace, and master's money-status and readmodels exports. - packages/gateway/src/routes/swf.ts: both sides made POST /api/swf/epochs/:epochId/distribute answer 501 not_available before anything is read. Master's hunk (#421's, reviewed and merged with #424) is kept. The behavior is identical. - packages/gateway/src/routes/rewards.ts: #360 made the reward, certificate and treasury routes answer 501 and deleted their fixtures. Master's N80 (#459, reviewed and merged) changed only the certificates: an empty list, 404 for one, and a 501 mint that claims nothing. It kept the reward and treasury fixtures. Kept: master's N80 certificates, and #360's 501s for rewards and treasury, with no fixture import. economics-no-fabrication.test.ts drops its three certificate rows (certificates-n80.test.ts pins N80), and the reward and treasury rows still assert 501. On the merged tree: spec 1286/1286; contracts 267/267; mcp-server 76/76; gateway 3717 passed, 13 skipped, 0 failed; dashboard 353/353; tsc clean for contracts, mcp-server, gateway and dashboard. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
N34: no route serves fixtures or random values as live data
Board N34 is the server side of PX-3. Steward ruling #2498: one
PCC_DEMO_ROUTESflag, and each family's owner decides later whether to wire it or retire it.Stack: on #373 (
fix/fiat-ramp-honesty@ d5be880). That PR carriesisDemoRoutesOn(), which ignores the flag underNODE_ENV=production. CI suites run only on PRs based on master (#2474), so the counts below are from local runs.What changes
With
PCC_DEMO_ROUTESunset, 71 routes in 10 families answer501 {error: "not_available", message, see}. A probe of every registered route in each family found these; the table lists them.seepointer isGET /api/carrier/shipments/:jobIdseepointer isGET /api/agents/live/conversationsPOST /roi;GET /categories(a real taxonomy, withcount: null+unavailableoutside demo)registered: false)In demo mode, the refused routes (except SWF distribution) answer their old examples, marked
mock: true, demo: truewith the headerx-pcc-demo: true.Other changes:
report_hintis not added to designed refusals (not_available,not_configured), so agents are no longer told to file a bug for them.seepointers. Each one names a registered live route, as METHOD + path.defaultedinputs and itsassumptions, and refuses a non-number with 400.demoOnlyEndpointsin the JSON form.docs/AGENT_INTEGRATION.mdmark each refused family and the MCP toolspcc_list_protocols,pcc_depin_stats,pcc_discover_scanandpcc_discover_onboard.Ratchet
n34-routes-fabrication-ratchet.test.tscountsmock*/MOCK_*/fake*/FAKE_*data bindings and valueMath.random()calls inroutes/, with comments stripped.Reviews (independent, per group)
routes.test.tspinned the old 200s; fixed in 08c0f68. The report-hint finding is fixed in bea9c87, and the context pack in 967fd7f.seepointer is fixed in cc78e3e, the ROI defaults in 4e649bc, and the MCP/agent-package flag is sent to aeo.createGateway()composition, every refused route answered 501 with zero fixture values, across path and method variants. Its wording nit is fixed in 7aaff8b, the batches finding in 8fa98ac, and the context pack in 967fd7f.Evidence
pnpm testgives 3706 passed / 6 skipped / 0 failed (220 files, solo run at 799f2f8; the same at 4e649bc). An earlier run at a load average of 32 timed out one unrelated test,settlement-crank-wiring, which passed 3/3 when run alone.NODE_ENV=production,PCC_DEMO_ROUTES=true(ignored in production) and a fresh DB, with a key provisioned throughPOST /api/auth/provision. Results:error,messageandsee: noreport_hint, no mock or demo mark.countunavailable, ROI withdefaulted, the registry summary with attestations unavailable, and batches[]./agent-context-pack.jsonlists 44demoOnlyEndpoints.returns/pcc-readmodels-work/n34-e2e-trace-20260924.txt.For other owners (not changed here)
swfAccrue("settlement", jobId, 1000, ...)accrues a constant 1000 per released milestone (settlement.ts:204, settlement.facade.ts:692), so every SWF money figure derives from it.GET /api/swf/summaryreportslastDistributionAt= now when nothing was distributed, and labels the in-memory ledger as a chain balance.seepointers.200 {error: "not_found"}for unknown ids.r.json()without checkingr.ok, so a 501 shows as empty data.🤖 Generated with Claude Code
https://claude.ai/code/session_01A2ZvsqsAb7jC7AC8Viisqn