Skip to main content

RFC Review: Midtrans Native Integration — Phase 1 (P0 Core Actions)

Companion review for midtrans-phase-1-p0-core-actions.md, produced by the rfc-reviewer skill. Lives beside the RFC; valid only for the RFC revision in reviewed_rfc_last_updated. This file records three cycles: R1 (initial draft) and R2 (post-iteration; reviewed against working tree at HEAD 392ffaa) in one prior session, and R3 — a cumulative DELTA re-review that re-grounds every citation against the MOVED chatbot HEAD fa6dd8b79 (top commit literally fix(service): skip actions with unresolved argument types).

Executive Summary

  • Overall Score: 7.0/10 (R3; R2 was 7.5, R1 6.5)
  • Rating: Strong (R3; R2 Strong, R1 Needs Work)
  • RFC Type: full-stack
  • Sub-Type: frontend new-feature · backend new-feature
  • Assessment Confidence: High (every RFC anchor re-opened against current chatbot fa6dd8b79 / chatbot-fe f5b80d5a; drift recorded per-row in §Evidence Notes and the citation table below)
  • Applied Caps/Gates: No hard cap triggers (no category < 5.0; the one cross-layer partial carries a documented mapper mitigation, so the 6.5 cross-layer-mismatch cap does not apply). The −0.5 vs R2 is a judgment move, not a cap: a newly-discovered, code-introduced execution gap (REV-9) lowers DMS/CNT/ACV confidence.
  • Implementation Readiness Verdict: PROCEED with notes — an agent can still build all 9 chunks against the shipped default-Bearer path; but one new pre-seed step is now mandatory (REV-9: each node_registries property must carry a non-blank type, else the action is silently dropped at AI-Service sync). The two prior external confirmations (REV-1, REV-7) remain open.
  • Report Path: chatbot/native-integration/rfcs/midtrans-phase-1-p0-core-actions-review.md
  • RFC Author: Dimas Fauzi Hidayat | Reviewed: 2026-06-20 (R3)

This RFC remains unusually well-grounded: after re-opening every anchor against the moved codebase, the executor framework (ActionExecutorFactory, NodeTypeRegistry::GROUP, NodeExecutorInterface), AuthorizedHttpClient, the shipped midtrans_oauth2.yml, the api_connections/node_registries/ ai_agent_tools mounts, the CTA-URL send path, events, flag, and encryption all still verify exactly as the RFC describes — citation health is high. The single material consequence of the codebase move is the top commit fa6dd8b79 "skip actions with unresolved argument types": FrontendService::V2::AiAgent::Repositories::SyncToAiService now filters out any action whose AI-resolvable arguments have a blank type, resolving that type for non-api actions from the matching node_registries row's properties[].type. The RFC's §2.3 properties[] spec lists fields only as field → html.element and omits the per-property type — so a Midtrans action seeded as written would be silently skipped from the agent's skill pack. That is the new R3 finding (REV-9) and the one thing that must be fixed before chunk 1 seeds the registry rows. Two minor citation paths drifted (FE actionIconConfig.ts moved under modules/bot-automation/utils/; the input-text shorthand in §2.3 must render as html.element:'input' + html.type:'text') — both recorded below.


Quick Verdict

Why this RFC can be implemented agentically:

  • Every execution chunk (§4.D) still has exact, re-verified file paths and repo-sourced commands; the executor pattern is copied from a real, still-present precedent (GoogleSheets::Execute).
  • The auth/HTTP seam, registry wiring, CTA-URL delivery, events, flag/plan gating, encryption, and rollback are all re-grounded to verified code at fa6dd8b79 — minimal guessing surface.
  • Failure handling is thorough and includes the 429 rate_limited path and pinned per-call request/response schemas (REV-2/REV-3 fixes confirmed to still hold).

Why this RFC will still cause agent guessing or a pre-merge pause:

  • NEW (REV-9): the seeded properties[] lack a per-property type, which the moved code (fa6dd8b79) now requires — actions would be silently dropped at AI-Service sync. This must be fixed in the §2.3 schema before chunk 1.
  • The live auth header for /v2/* is confirmed only after a Midtrans-docs check (REV-1) — both outcomes pre-specified.
  • The Admin credential field set depends on REV-1/REV-7; chunk 7 form copy is provisional.
  • properties[] field names remain provisional pending Midtrans docs (OQ-6 / REV-8).

Findings Ledger (carry-forward)

IDSeverityFinding (one line)RFC locationStatusFirst seenResolved inEvidence / fix
REV-1major (was blocker)Auth header for Midtrans v2 (Bearer vs Basic) unconfirmed — gated chunks 3–6§2.4 external calls, OQ-3open (de-risked)R1— (mitigated R2)R2 added §2 Decision 8: both Bearer (shipped) + midtrans_basic fallback specified; executor auth-agnostic via AuthorizedHttpClient → config swap, not code change. R3 re-verified: midtrans_oauth2.yml (header_strategy: bearer_token, oauth2_client_credentials, ttl_seconds:3300) + AuthorizedHttpClient.call(...max_retries:1, call_options:{}) + AUTH_FAILURE_CODES=%w[401 403] all still present. Residual = 1-line doc confirmation.
REV-2majorPer-call Midtrans request/response schemas not pinned (ACV)§2.4fixedR1R2R3 confirms §2.4 still carries concrete JSON request/response per call incl. consumed fields + error shapes. Fix holds.
REV-3majorMidtrans rate-limit / 429 handling unspecified§3 Perf, §3.A, §3.BfixedR1R2R3 confirms 429 → no-auto-retry policy + distinct rate_limited failure_reason + copy + catalog rows still present; AuthorizedHttpClient retries only on 401/403 (verified AUTH_FAILURE_CODES). Fix holds.
REV-4minorNo FE browser-support matrix / perf budget rationale (NFS)§3 PerformancefixedR1R2R3 confirms browser matrix + perf n/a rationale still present. Fix holds.
REV-5minorFE config-panel analytics not defined (OBS)§3 MonitoringfixedR1R2R3 confirms reuse-existing-analytics note + optional midtrans_action_configured event still present. Fix holds.
REV-6minorapi_connections → connect-form casing/derivation gap (cross-layer ACV)§2.Gaccepted-riskR1R2R3 re-verified: POST /v1/api_connections mounted (api.rb:28); FE-side mapper deriving environment/connected still the specified mitigation; no BE change. Acceptable.
REV-7majorPRD auth fields/TTL/endpoint mismatch needs PM reconciliationgrounding note, OQ-2openR1External: PM must confirm field set; coupled to REV-1. R3 re-verified the grounding-note table is still accurate vs shipped midtrans_oauth2.yml (environment/client_id/client_secret, /v1/oauth/token, 3300 s).
REV-8minorproperties[] field names provisional§2.3, OQ-6openR1External: confirm against Midtrans docs; schemas pinned in §2.4 reduce gap. (Distinct from REV-9, which is about the missing type key, not the names.)
REV-9majorproperties[] rows omit the per-property type that fa6dd8b79 now requires — non-api actions whose AI-resolvable args resolve to a blank type are silently filtered out of the AI-Service sync (args_with_unresolved_type?), so the Midtrans actions would never reach the agent.§2.3 properties[] table; §2.DfixedR3RFC rev 2026-06-21NEW (codebase moved 392ffaa→fa6dd8b79). app/api/frontend_service/v2/ai_agent/repositories/sync_to_ai_service.rb: build_skill_actions now uses filter_map + next if args_with_unresolved_type?(args); build_non_api_args sets type: map_param_type(registry_prop&.dig('type')) from the matching node_registries properties[].type. Factory with_properties shows canonical shape {name,type,required}; FE PropertiesItem also requires non-optional type:string. RFC §2.3 listed only field → html.element. FIXED (RFC last_updated 2026-06-21): §2.3 rewritten with a per-property type column (string/integer/array/object), the grounded canonical JSON shape, and the SyncToAiService drop rationale cited to :404-467; §4.D chunk 1 acceptance now asserts a use_ai Midtrans action survives build_skill_actions (args_with_unresolved_type?false); OQ-8 → resolved. (Field names still tracked by REV-8/OQ-6.)

Ledger summary: 2 open (REV-1, REV-7 — both external confirmations), 1 newly fixed (REV-9, fixed in RFC rev 2026-06-21; the R2 fixes REV-2..REV-5 also re-confirmed to hold), 1 accepted-risk (REV-6), 1 minor open (REV-8). Still-open material findings (REV-1, REV-7) remain promoted to the RFC's §5 Open-Questions table by id. Note: REV-9 was fixed by an RFC edit made after the R3 scoring pass (RFC last_updated advanced 2026-06-20 → 2026-06-21); a fresh R4 pass should re-score DMS/CNT/ACV (R3 had docked them −0.5 for REV-9) and confirm the chunk-1 sync-survival rspec lands during implementation.


Citation Re-Grounding (R3 — codebase moved 392ffaa → fa6dd8b79)

The core of this cycle. Every anchor re-opened against current HEAD. ✓ valid / ⚠ drifted / ✗ broken.

Anchor (RFC cite)Current codeStatus
config/auth_providers/midtrans_oauth2.yml (bearer_token, oauth2_client_credentials, ttl_seconds:3300, sandbox/prod /v1/oauth/token, fields environment/client_id/client_secret)identical✓ valid
OrganizationConnection#auth_type_enum contains midtrans_oauth2%w[… midtrans_oauth2 google_oauth2]✓ valid
lib/auth/authorized_http_client.rbcall(credential:, request:, http_client:, max_retries:1, call_options:{}); AUTH_FAILURE_CODES=%w[401 403]identical✓ valid
lib/auth/token_manager.rb + token_stores/redis_store.rb + payload_encryptor.rball present✓ valid
NodeTypeRegistry::GROUP (RFC quotes {'api','mekari_qontak_crm','google_sheets',…}){'api','mekari_qontak_crm','mekari_qontak_chat','google_sheets'} — same shape; midtrans correctly absent (to be added by chunk 1)✓ valid
ActionExecutorFactory.for resolves NodeRegistry.find_by_type_and_version(action_type)&.node_type_group → GROUP[group], constantize under PREFIXidentical logic (api special-cased)✓ valid
NodeExecutorInterface keyword init (organization_id:, credential:, parameters:, arguments:, room_id:, node_type:)present at nodes/node_executor_interface.rb✓ valid
GoogleSheets::Execute group executor dispatching on @node_typeinclude NodeExecutorInterface/ArgumentInterpolation; case @node_type; credential[:id] = OrganizationConnection id✓ valid
db/schema.rb node_registries (node_type, node_type_group, properties jsonb, enabled, unique (node_type, version))present (schema comment "Maps to an executor class via NodeTypeRegistry")✓ valid
GET /v1/node_registries / POST /v1/api_connections / /v1/ai_agent_tools mountsapi.rb:51 / :28 / :46 — exact lines match✓ valid
send_message_interactive.rb send_button_message(..., link:)present✓ valid
feature_flag.rb FeatureFlag.enabled?(group, code, default:)present✓ valid
NodeRegistry.find_by_type_and_versionapp/models/node_registry.rb:8✓ valid
Action arguments/argument-type resolution (RFC Assumption 3: args channel like API/Sheets)CHANGED by fa6dd8b79: SyncToAiService#build_non_api_args resolves arg type from node_registries properties[].type; build_skill_actions now drops actions with any blank arg type (args_with_unresolved_type?). RFC §2.3 omits property type.drifted → REV-9 (new major)
FE utils/actionIconConfig.ts SOURCE_ICON["Google Calendar"] (Source Verification row)file is at modules/bot-automation/utils/actionIconConfig.ts (not repo-root utils/); content confirms SOURCE_ICON["Google Calendar"] → /icons/google-calendar.svg + a default fallback⚠ minor path drift (claim otherwise correct)
FE ActionIntegrationForm.vue loops requiredItem.properties[] by prop.html.elementpresent; but text inputs key on html.element === 'input' && html.type === 'text' (not a single 'input-text' token)⚠ §2.3 input-text shorthand must map to html.element:'input' + html.type:'text'
FE NewActionDrawer.vue / CreateActionForm.vue / ActionListItem / ai-agents.ts (fetchActionList → node_registries) / ai-agent-tools.ts / nlp-feature.tsall present; ActionListItem/PropertiesItem shapes match (properties?: PropertiesItem[], PropertiesItem.type:string + html{})✓ valid
FE: "no Midtrans MidtransAuthBlock.vue in production" (net-new)absent (correct — net-new)✓ valid

Net: 16 anchors ✓, 1 ✗-equivalent drift that is genuinely material (REV-9), 2 minor path/shorthand drifts. The "unresolved argument types" fix does impact the RFC's action-argument assumptions — see REV-9.


PRD → RFC Traceability Matrix

Standard format (PRD exists)

PRD ElementRFC SectionCoverage
§6 four P0 action types§1 PRD-to-Schema rows 1–8, §2.3 seed rows, §2.4Full (but see REV-9 — seeded properties[] need type to actually sync)
MIDTRANS-S01 (configure action)§1 Detail 1.A/1.C, §2.A, §2.4Full
MIDTRANS-S02 (check status)§2.2 seq-1, §4.D chunk 3Full
MIDTRANS-S03 (payment link + CTA-URL)§2.2 seq-2, §4.D chunk 4Full
MIDTRANS-S04 (cancel + confirm)§2.1 branch, §2.2 seq-4, §4.D chunk 5Full
MIDTRANS-S05 (refund + confirm + idempotency)§2.D, §2.2 seq-4, §4.D chunk 6Full
§7 idempotency / rate-limit / partial-refund§2 Decision 4, §3 (rate-limit), §5 OQ-5Full (partial deferred with documented default)
§10 observability events§3 Monitoring (5 events incl. rate_limited)Full
NEG-1..4§1 Detail 1.A, §3Full
§12 launch stage gates§4 (signals)Partial — schedule intentionally deferred to delivery/ (RFC scope rule)

Summary: 9 of 9 core PRD areas fully covered; 1 intentionally-partial. Reverse: every new artifact traces to a PRD AC; refund_key dedupe is RFC-introduced and justified vs PRD §7. No scope-creep. The only new caveat is execution-time (REV-9), not coverage.

PRT score: 9.0 (unchanged).


Scorecard

Full-Stack Scorecard (18 categories) — R3

#CategorySourceScore (R2→R3)Evidence-Based Rationale
1PRT — PRD TraceabilityMerged9.0FE: Detail 1.A surface/role coverage · BE: PRD-to-Schema + per-section coverage; both directions present. Re-verified.
2TDC — Technical DecisionsMerged8.08 ADR blocks w/ options+reversibility; Decision 8 closes auth strategy. Re-grounded; still resolved.
3CNT — Contract SpecificityFE7.0→6.5MidtransAuthBlock props/events typed (§2.A); but the registry-driven properties[] (§2.3) omit per-property type that FE PropertiesItem requires and BE sync now enforces (REV-9); input-text shorthand drifts from html.element:'input'+html.type:'text'.
4SCB — Scope BoundariesFE8.0§2.I lists FE create/modify/NOT-touched explicitly. Re-verified.
5DEP — DependenciesFE+BE avail8.5§1 Dependencies table tags each as exists/needs-confirming; every "exists" row re-opened and confirmed at fa6dd8b79.
6NFS — Non-FunctionalFE7.5Browser matrix + perf n/a rationale + a11y (§3.E). Unchanged.
7TPS — Test PlanFE7.0§4.C commands repo-sourced (vitest/rspec/build); cross-layer manual sandbox. Gap surfaced: no test asserts the seeded actions survive SyncToAiService filtering (REV-9). FE unit specifics still light.
8DMS — Data Model & SchemaBE7.5→7.0No new tables; seed rows + schema ref + per-status note. Seed properties[] are now functionally incomplete (missing type) given the moved code — would sync-skip the action (REV-9).
9ACV — API Contract & VersioningBE7.5§2.4 per-call request/response schemas + Decision 8 dual-auth re-verified; external contract concrete. The internal ActionExecute→sync contract gained a new precondition (REV-9) but the HTTP contracts themselves are intact.
10DIC — Data Integrity & ConsistencyBE8.5§2.D integrity matrix; refund refund_key + redis dedupe; cancel natural idempotency. Re-verified.
11FMC — Failure Mode CoverageMerged8.5BE failure+branch+error catalogs; FE error catalog; 429 rate_limited; FE↔BE code-shape check. Re-verified.
12CSS — Concurrency & ScalingBE7.5§2.E collision map (token herd, refund dedupe); explicit 429 policy. Unchanged.
13SAS — Security & AuthorizationBE8.5Role×endpoint matrix, SSRF mitigation (no free-form URL), Lockbox encryption (payload_encryptor.rb verified), masked reads, brakeman.
14ROL — Rollout & RollbackMerged8.5Deploy order (BE-first) + cross-layer compat matrix + flag + ordered rollback recipe. Re-verified.
15OBS — ObservabilityMerged7.5BE events/logs/alerts thorough; FE analytics note lifts the BE-only ceiling. Unchanged.
16SBC — Service Boundary & CouplingBE8.0Sync-vs-async (Decision 5), single-squad boundary, executor seam. Re-verified.
17CPA — Pattern AlignmentMerged8.5Patterns-to-Follow table both layers, each citing a read reference file; all reference files re-opened and present.
18CDG — Compliance & Data GovernanceBE8.0Triggered (payment); §3.D — no PAN stored, only encrypted merchant credential; scope bounded.

Resource & Cost Advisory

  • Synchronous execution holds a pod ≤60 s per in-flight action; fine at P0 volume (RFC §4.F / §5). Non-blocking.

Decision Closure Assessment

Decision Index

#DecisionStatusCritical Gaps
1Single Midtrans::Execute group executorResolvednone — mirrors GoogleSheets::Execute (re-verified present)
2Outbound via AuthorizedHttpClientResolvednone (signature re-verified)
360 s timeout via call_optionsResolvedconfirm call_options forwards to ::Http#call (noted in RFC; call_options param re-verified present on AuthorizedHttpClient)
4Refund idempotency (refund_key + dedupe)Resolvednone
5Synchronous executionResolvednone
6Feature flag + plan gatingResolvedflag name proposed, confirm (OQ-1)
7Multi-tenancy isolationResolvednone
8Auth header (Bearer + Basic fallback)Resolved (with 1 external confirmation)REV-1 doc check before live wiring; both paths specified

Aggregate: 8 of 8 decisions Resolved, 0 Partial, 0 Dangling. R3 note: no decision regressed, but a new implicit precondition (every registry property needs a type so the action survives SyncToAiService) is not captured by any decision and surfaced as REV-9.


Decision: 8 — Auth header strategy (the R1→R2 keystone; R3 re-grounded)

Status: Resolved (re-verified against fa6dd8b79)

What was decided — Default to Bearer via the shipped midtrans_oauth2 descriptor; specify a drop-in midtrans_basic descriptor as the fallback, selected by a single confirmation. The executor is auth-agnostic because it calls AuthorizedHttpClient.call(credential:, request:).

R3 groundingconfig/auth_providers/midtrans_oauth2.yml (header_strategy: bearer_token, token_flow.strategy: oauth2_client_credentials, ttl_seconds: 3300, env→/v1/oauth/token map), lib/auth/authorized_http_client.rb (call(... max_retries:1, call_options:{}), AUTH_FAILURE_CODES=%w[401 403]) — all present and unchanged. The fallback YAML is fully written out in the RFC.

Failure handling — 401/403 retry-once (verified existing); 429 → rate_limited (no retry); auth failure → auth_error.

Challenge resultsScale: read-heavy, low volume — holds. Reversibility: trivial (auth_type swap). Consistency: aligns with grounding note + OQ-2/OQ-3. Agent implementability: an agent can build against the default Bearer path immediately; only the live-header wiring waits on REV-1.

Gaps / suggested resolution — Get the Midtrans-docs confirmation (REV-1); if Basic, confirm server_key (REV-7).

(Decisions 1–7 are all Resolved with grounded references re-opened at fa6dd8b79; abbreviated per the rubric's ≥7.0 optimization.)


Cross-Layer Contract Verification

EndpointBackend Response SchemaFrontend Expected SchemaMatch?Gaps
GET /v1/node_registriesrows {node_type,name,categories,properties,settings,version}ActionListItem {node_type,name,categories[],version,inputs[],outputs[],properties?:PropertiesItem[],settings?} (re-verified L49–66)Yessnake_case already consumed; but PropertiesItem.type is non-optional (L4) and the seeded rows must populate it — same root as REV-9
POST /v1/api_connections{id,auth_type,auth_data(masked),name,code}connect form expects {id,environment,connected}PartialFE-side mapper derives environment/connected — specified in §2.G (REV-6, accepted)
POST /v1/ai_agent_tools{id,action_type,name,parameters,credentials}create payload mirrorYesexisting CRUD
(internal) ActionExecuteSyncToAiServiceaction synced only if every AI-resolvable arg has a non-blank type (fa6dd8b79)n/a (agent-side)No (new)REV-9 — registry properties[].type must be set or the action is filtered out before reaching the agent

Mismatches found: 1 prior partial (mapper-mitigated, no cap) + 1 new functional mismatch (REV-9) between the seeded property schema and the post-fa6dd8b79 sync contract. REV-9 is a schema-completeness gap fixable in the RFC, not a backend-vs-frontend shape contradiction, so it does not trip the 6.5 cross-layer-mismatch cap — but it is a blocker for chunk 1 correctness.


Cross-Layer Rollout Compatibility Matrix

ScenarioFrontendBackendWorks?Notes
Pre-deployOldOldYesbaseline
Backend firstOldNewYesregistry rows inert behind flag OFF
Frontend firstNewOldNoavoided by deploy order (BE first)
Both deployedNewNewYestarget
Backend rollbackNewOldPartialflag OFF hides actions
Frontend rollbackOldNewYesBE inert behind flag

Deploy order: Backend first (specified, §4 + Detail 4.A). The one "No" is explicitly avoided by deploy order → ROL not capped. Re-verified unchanged.


Customer message → AI Agent identifies payment intent
→ [config-time] action synced to AI Service ONLY if every arg has a non-blank type (fa6dd8b79 — REV-9)
→ internal ActionExecute (flag + plan gate)
→ Midtrans::Execute#midtrans_create_payment_link
→ AuthorizedHttpClient.call(POST /v1/payment-links {transaction_details,…}, Bearer/Basic)
→ Midtrans 201 { payment_url }
→ SendMessageInteractive#send_button_message(link: payment_url) [WhatsApp] / text [other]
→ SendMixpanelEventWorker(midtrans_payment_link_created {payment_link_url, channel_type})

Gaps in flow: the runtime hops still name real, re-verified files. The new gap is one step earlier, at config/sync time: REV-9 (a property without type ⇒ the action is dropped from the synced skill pack, so the agent never invokes the executor at all). Same caveat applies to status/cancel/refund.


Agentic Readiness Deep-Dive

Vague Word Audit

Spec sections are typed/measured; "TBD" appears only inside explicitly-labelled Open Questions. Vague words in spec sections: ~0.

Dangling Alternatives

0 — Decision 8 resolves the only R1 dangling alternative.

Task Decomposition Assessment

9 ordered chunks (§4.D), each with files + repo-sourced commands + assertable criteria. Verifiable. R3 addition: chunk 1's acceptance criteria should be extended to assert the seeded rows carry a type per property and that SyncToAiService does not skip the Midtrans actions (REV-9).


Strengths

  • Citation health survived a codebase move — re-opening every anchor at fa6dd8b79 (vs prior 392ffaa), the entire BE execution/auth framework and all four api.rb mounts still verify line-for-line. This is rare and is the RFC's biggest asset.
  • Intellectual honesty on the PRD-vs-code gap — the §1 grounding note still matches the shipped midtrans_oauth2.yml exactly (environment/client_id/client_secret, /v1/oauth/token, 3300 s).
  • Strong safety design for irreversible actions — status pre-check + confirmation + refund refund_key/dedupe (§2.D, Decision 4), with webmock-assertable "endpoint NOT called" criteria.

Biggest Gaps

  • REV-9 (NEW, major): the §2.3 properties[] rows omit the per-property type the moved code now requires; a Midtrans action seeded as written is silently filtered out of the AI-Service sync (args_with_unresolved_type? in sync_to_ai_service.rb). Highest-impact gap this cycle — it would make the feature appear configured but non-functional.
  • REV-1 / OQ-3 (de-risked, external): the live auth header on /v2/* still needs a one-line Midtrans-docs confirmation; default Bearer is shipped, fallback is specified.
  • REV-7 / OQ-2 (external): PM must reconcile the PRD's server_key-era field names with the chosen descriptor so chunk 7's credential form is built once.

Priority Actions

  1. REV-9 / §2.3 properties[] — add an explicit non-blank type (e.g. string/integer) to every seeded property, matching the factory shape {name,type,required} and the FE PropertiesItem.type. Extend chunk 1 acceptance criteria with an rspec asserting SyncToAiService does NOT skip the seeded Midtrans actions (args_with_unresolved_type? returns false). Do before chunk 1 seeds the rows.
  2. REV-1 / §2.4 + Decision 8 — confirm against Midtrans docs whether /v2/* accepts the OAuth2 Bearer token; else point the credential at midtrans_basic (already specified). Unblocks live wiring on chunks 3–6.
  3. REV-7 / OQ-2 — PM confirms the credential field set so the FE form copy (chunk 7) is final.
  4. §2.3 input-text shorthand / §2.0 FE Source Verification path — clarify that order_id → input-text renders as html.element:'input' + html.type:'text' (per ActionIntegrationForm.vue), and fix the actionIconConfig.ts path to modules/bot-automation/utils/actionIconConfig.ts.
  5. OQ-6 (REV-8) — confirm properties[] field names/optionality against Midtrans docs.
  6. OQ-1 — confirm feature-flag name rollout / midtrans_native_action.

Backend Contract Addendum

Endpoint Contract Details (external Midtrans calls — pinned in §2.4, re-verified)

EndpointMethod/PathAuthZRequest ContractResponse ContractError ContractIdempotency/VersioningStatus
Check statusGET /v2/{order_id}/statusBearer/Basic (Decision 8)no body{order_id, transaction_status, status_code,…}404 not-found, 429 rate_limitedread; n/aComplete (confirm field casing OQ-6)
Create linkPOST /v1/payment-linksBearer/Basic{transaction_details{order_id,gross_amount}, item_details?, customer_details?,…}{order_id, payment_url}400 error_messagesn/aComplete
CancelPOST /v2/{order_id}/cancelBearer/Basicno body{status_code, transaction_status}412 invalid statusnatural (status pre-check)Complete
RefundPOST /v2/{order_id}/refundBearer/Basic{refund_key, amount?, reason?}{status_code, transaction_status, refund_key}412/400refund_key + redis dedupeComplete

Database / Seed Changes Details

ChangeTable/EntityDDL / Shape DiffData Migration PlanRollback PlanCompatibility WindowStatus
Seed 4 catalog rowsnode_registriesrows only (no DDL); each properties[] entry MUST include a non-blank type (REV-9) plus the html block FE rendersidempotent data migration on (node_type, version)set enabled=false / db:rollbackinert behind flagMissing type in seeded properties (REV-9)

Implementation Readiness Checklist

Unblocked (agent can proceed)

  • PRD → RFC traceability complete (forward + reverse)
  • All technical decisions resolved (8/8; Decision 8 with 1 external confirmation)
  • Failure modes handled with error catalog (incl. 429 rate_limited)
  • Configuration contract (flag + env + per-org credential) documented
  • Rollout plan with deploy order, flag, ordered rollback
  • Observability events + alerts defined (BE) + FE analytics note
  • Cross-layer contract verified (1 partial w/ mapper mitigation)
  • Deploy order specified (BE-first); compat matrix has no unaddressed "No"
  • End-to-end data flow traceable for each action
  • Security: authZ matrix, SSRF mitigation, tenancy isolation, secret encryption
  • Compliance handled (payment-adjacent; no PAN stored)

Blocked (must fix first)

  • REV-9 — seed properties[] must carry a per-property type, else the Midtrans actions are silently dropped at AI-Service sync (fa6dd8b79). Fix the §2.3 schema + add the sync-survival assertion to chunk 1 before seeding.
  • Before live auth wiring (chunks 3–6): REV-1 doc confirmation.
  • Before FE credential form (chunk 7): REV-7 PM field reconciliation.

Verdict: Ready to implement against the default Bearer path AFTER closing REV-9 in the §2.3 seed schema; close REV-1/REV-7 before finalizing the live auth header and credential form.


Task Manifest

The RFC's §4.D Agent Execution Plan is complete and well-ordered (9 chunks, files + commands + acceptance criteria) and its file paths re-verified at fa6dd8b79. One amendment: chunk 1 must seed properties[] with a per-property type and assert the seeded Midtrans actions survive SyncToAiService filtering (REV-9) before the rest proceeds. Chunks 3–6 default to Bearer and need only REV-1 before live wiring; chunk 7 (FE credential form) needs REV-7 for final field copy.


Dangling Decisions Log

#DecisionLocationOwnerDeadline
None dangling. REV-1 (auth confirmation) and REV-7 (field reconciliation) are external confirmations; REV-9 is a seed-schema correction, not an unresolved design.§2.4 / §2.3 / OQ-2/OQ-3BOT Eng + PM2026-06-27

Open Questions

#QuestionCategorySeverity
REV-9Do the seeded node_registries properties[] carry a non-blank type per property, so SyncToAiService (fa6dd8b79) does not silently skip the Midtrans actions?DMS/CNT/ACVBlocking (for chunk 1)
REV-1Does the OAuth2 Bearer token authenticate Midtrans /v2/* + /v1/payment-links? (else use midtrans_basic)ACV/TDCImportant
REV-7Reconcile PRD auth field names/TTL/endpoint with the chosen descriptor; finalise FE form copyTDC/CNTImportant
OQ-6 (REV-8)Confirm properties[] field names/optionality vs Midtrans docsDMS/CNTImportant
OQ-1Confirm feature-flag name rollout / midtrans_native_actionROLNice-to-have
OQ-5Partial vs full refund scope for P0DICNice-to-have

Evidence Notes

  • app/api/frontend_service/v2/ai_agent/repositories/sync_to_ai_service.rb (NEW at fa6dd8b79)build_skill_actions now filter_maps with next if args_with_unresolved_type?(args); build_non_api_args derives type: map_param_type(registry_prop&.dig('type')) from node_registries properties[].type. This is the single material consequence of the codebase move and the basis for REV-9. Lowered DMS 7.5→7.0 and CNT 7.0→6.5.
  • config/auth_providers/midtrans_oauth2.yml, lib/auth/authorized_http_client.rb, node_type_registry.rb, action_executor_factory.rb, nodes/node_executor_interface.rb, google_sheets/execute.rb, app/api/frontend_service/api.rb (mounts 28/46/51), node_registry.rb — all re-opened at fa6dd8b79; match the RFC anchors. Sustained the high PRT/CPA/SAS/DEP scores.
  • spec/factories/node_registry.rbwith_properties trait shows canonical property shape {name, type, required}, confirming type is an expected property key (corroborates REV-9).
  • chatbot-fe modules/bot-automation/utils/actionIconConfig.ts, ActionIntegrationForm.vue, bot-automation-actions-constants.tsactionIconConfig.ts path drifted from the RFC's utils/...; PropertiesItem.type is non-optional and text inputs render via html.element:'input'+html.type:'text' (not 'input-text'). Minor §2.3/§2.0 corrections.
  • §2.4 / §2 Decision 8 / §3 (R2 fixes) — re-confirmed present and consistent; REV-2/3/4/5 remain fixed.

Review History

CycleDateReviewed RFC revision (last_updated / commit)ScoreVerdictFindings open → fixedNotes
R12026-06-20last_updated 2026-06-20 (initial draft)6.5HOLD8 openStrong grounding; ACV weak (5.5) + auth mechanism a self-declared blocker → HOLD
R22026-06-20last_updated 2026-06-20 (post-iteration; HEAD 392ffaa working tree)7.5PROCEED with notes4 fixed, 1 de-risked, 1 accepted-risk, 2 open (external)Decision 8 dual-auth, pinned §2.4 schemas, 429 policy, FE perf/analytics
R32026-06-20last_updated 2026-06-20 (re-grounded vs MOVED HEAD fa6dd8b79; FE f5b80d5a)7.0PROCEED with notes0 newly fixed (R2 fixes re-confirmed), 3 open (REV-1/REV-7 external + new REV-9 major), 1 accepted, 1 minor openRe-grounded every citation: 16 ✓, REV-9 drift from fix(service): skip actions with unresolved argument types — seeded properties[] need a per-property type or actions are silently sync-skipped; 2 minor FE path/shorthand drifts. −0.5 vs R2.