KB-1ECF

S177-R0 — Code Reconcile Report (2026-05-19)

11 min read Revision 1
larks177r0code-reconcilestopmaterial-drift

S177-R0 — Code Reconcile Report

Date: 2026-05-19 Scope: Read-only verification of live lark-client source vs PATCH1 assumptions. No code, no Lark write, no commit, no deploy, no self-advance. Verdict: MATERIAL DRIFT → STOP. DO NOT start Sprint 1. PATCH2 required. Pairs with: s177-oq-decision-record-2026-05-19.md


0. Method & access note

The VPS read_file MCP tool did resolve /opt/incomex/lark-client/** source files (this corrects the earlier base-doc assumption that the path was unreadable — that assumption was never probed; it is now disproven). Source was inspected directly via file reads. Files read & verified: README.md, requirements.txt, pyproject.toml, version.txt, lark_client/__init__.py, core.py, exceptions.py, audit.py, reader.py, registry.py, cli/lark_tool.py, config/allowed_endpoints.yaml, config/bases.yaml, tests/test_core.py.

Git identity (repo root / branch / HEAD / status): NOT DETERMINABLE from this environment. No shell/exec tool; the VPS MCP exposes no git tool; local cwd is not this repo. Reported as unverifiable, not assumed. (Does not affect source-level reconcile, which was done by reading the actual files.)

1. Confirmed facts (live source)

  • Package lark-client v1.0.0 (version.txt), Python >=3.10, deps requests>=2.28, PyYAML>=6.0, tabulate>=0.9; dev pytest>=7.0. Entry point cli.lark_tool:main (pyproject.toml [project.scripts]).
  • lark_client/__init__.py exports LarkCore, LarkReader, Registry, AuditLogger.
  • LarkCore (core.py): credential chain env → /opt/incomex/docker/.env → GSM (gcloud secrets ... --project=github-chatgpt-ggcloud); token cache w/ 300s margin; endpoint whitelist loaded from config/allowed_endpoints.yaml, iterates both read: and write: sections (write section already supported, currently empty); rate limiter = fcntl.flock on /var/lock/lark-api.lock + token-bucket state /var/lock/lark-api.state, _MAX_RPS=10, waits ≤~10s; retry on {429,503} ×3 backoff [1,2,4] + ConnectionError/Timeout; private _request(method, endpoint, *, json_data, params, timeout, _audit_cmd) and paginate(...). requests imported inside core only (consistent with the "no direct requests outside core" rule).
  • exceptions.py: base LarkClientError; subclasses EndpointNotAllowed, CredentialPermissionLost, TokenRefreshError, LarkAPIError(status_code, code, msg), RateLimitExceeded.
  • AuditLogger (audit.py): append-only JSONL /var/log/lark-ops/YYYYMMDD.jsonl; public log_call(agent, command, endpoint, method, status, duration_ms, api_calls, error, request_id) and log_cli_invocation(...); private _write(entry) masks credential-like 30+ char strings (skips ts/agent/cmd). No planned/started (2-phase) method; fixed call-oriented schema.
  • LarkReader: list_tables, list_fields, search_records — read-only, via core.paginate.
  • Registry (registry.py): Registry.load()config/bases.yaml; BaseEntry(key,name,app_token,role,known_table_count,note); get_by_key / get_by_app_token raise KeyError; list_all, list_by_role.
  • CLI (cli/lark_tool.py): argparse with nested subparsers; per-command cmd_* functions; manual dispatch in main(argv); exit codes EXIT_OK=0, EXIT_USER_ERROR=1, EXIT_NETWORK_API=2, EXIT_INTERNAL=3, EXIT_PERMISSION_CONFIG=4, EXIT_CRED_LOST=5; existing groups registry, schema, audit; submodules cli.dump, cli.summarize. No Click anywhere.
  • config/allowed_endpoints.yaml: write: [] (empty); reads incl. tables/fields/views/records/search/workflows.
  • config/bases.yaml: 18 bases. Base đệm CONFIRMED: key 88-phai-cu-base-dem, app_token Nf2bb1ExXaYnlksgoyQl72GNgAc, role staging. Production 88-phai-cu token YSIkb8PxOaNaozs2vwalOOcagkf, role core. (Base đệm is detectable by key OR role=="staging".)
  • Tests (tests/test_core.py): pytest; mix of live-API integration (test_token_obtainable hits real Lark, no mock, ungated) and offline whitelist tests; no conftest.py; no LARK_TEST_INTEGRATION gating convention exists.

2. Reconcile matrix

# PATCH1 assumption Live source finding Match/Drift Adjustment needed Blocker?
1 CLI = Click group in cli/lark_tool.py; "register into existing Click group" argparse subparsers + cmd_* + manual dispatch; no Click MATERIAL DRIFT Re-express all Track-B CLI (records/fields) as argparse subparsers + cmd_records_*/cmd_fields_*, registered on existing subparsers; add --data/--approval/--no-dry-run/--confirm via add_argument YES
2 Exit codes 3=abort,4=api,5=partial,0=ok (invented) 0 OK,1 USER_ERROR,2 NETWORK_API,3 INTERNAL,4 PERMISSION_CONFIG,5 CRED_LOST MATERIAL DRIFT Remap: approval/safety abort → reserve a new code or reuse 1; LarkAPIError → 2; partial-failure → new dedicated code (extend enum, don't collide); CRED_LOST stays 5 YES
3 New errors derive from generic "exceptions base"; LarkApiError base = LarkClientError; API error = LarkAPIError (caps) DRIFT (naming/parent) New ApprovalError/SafetyViolation/PartialFailureError/AuditWriteError subclass LarkClientError; reference LarkAPIError (correct caps) partial
4 SafetyLayer wraps a LarkCore mutating call; idempotency/client_token "where supported" No public write method; _request private, no idempotency/client_token param; write whitelist empty MATERIAL DRIFT (LarkCore) Add a guarded write entrypoint to LarkCore (or sanctioned use of _request) plus idempotency support (header/body); add write endpoints to allowed_endpoints.yaml write: YES
5 2-phase audit (planned/post) + emergency + orphan log via audit AuditLogger has only post-hoc log_call; fixed schema; _write private + masks 30+ char strings MATERIAL DRIFT (audit harness) Extend AuditLogger with log_write_planned() / log_write_result() / emergency + orphan writers; design new entry schema; verify masking won't corrupt idempotency_key/backup_ref YES
6 Global file-lock rate limiter 10 rps /var/lock/lark-api.lock + .state, token-bucket 10 rps, ≤10s wait MATCH none (note: lock released after state update, not held over API call — irrelevant to rate-limit; per-record lock still net-new as PATCH1 already states) no
7 Registry/bases.yaml; raise UnknownBaseError Registry.load()/bases.yaml; raises KeyError; has role; get_by_app_token exists DRIFT (minor) Use existing KeyError (or wrap into UnknownBaseError(LarkClientError)); use role=="staging"/key for Base-đệm guard partial
8 Base đệm token Nf2bb1ExXaYnlksgoyQl72GNgAc CONFIRMED in bases.yaml, key 88-phai-cu-base-dem, role staging MATCH none no
9 Tests "mirror 19/19+8/8 mocked style"; gated integration via LARK_TEST_INTEGRATION=1 pytest; ungated live-API tests; no conftest; no such env gate exists MATERIAL DRIFT (test harness) Introduce LARK_TEST_INTEGRATION gate + Base-đệm token hard-assert as a new convention; add conftest.py; do not assume mocked baseline — current test_token_obtainable calls real Lark YES
10 "no import requests outside core" core imports requests; nothing else does MATCH keep new write/service code free of direct requests no
11 config dir config/ holds bases.yaml + allowed_endpoints.yaml confirmed (_CONFIG_DIR = .../config) MATCH new write-approvals.yaml/pii-fields.yaml/lark-api-limits.yaml go in same config/ no

3. STOP determination

STOP rule (PATCH1 §I.0): material drift in LarkCore, registry, CLI, or test harness ⇒ STOP, do not code.

Material drift found in CLI (argparse≠Click), LarkCore (no write/idempotency, private _request), audit harness (no 2-phase), test harness (ungated live tests, no gate convention), plus exit-code/exception-contract drift. → STOP CONFIRMED. No Sprint 1 code.

None of the drifts are fatal to the architecture — SafetyLayer / service / DI / GPG / approval / PII design all remain valid. They are interface-binding corrections: the design must be re-expressed against argparse, the real exception/exit-code contract, an extended AuditLogger, a new LarkCore guarded-write+idempotency entrypoint, and a newly-introduced test-gating convention.

4. Required: PATCH2 (before any code or commit)

PATCH2 must:

  1. Replace §G.3 Click design with argparse subparser design (record/field commands as cmd_*, registered on existing subparsers, exact arg flags).
  2. Replace §B.3 exit-code table with the actual enum + a sanctioned extension for approval/safety/partial-failure codes.
  3. Correct exception names/parents: subclass LarkClientError; use LarkAPIError; specify UnknownBaseError wrapping KeyError (or use KeyError).
  4. Specify AuditLogger extension (new 2-phase/emergency/orphan methods + entry schema + masking-safety for idempotency_key/backup_ref).
  5. Specify LarkCore guarded-write entrypoint + idempotency/client_token mechanism and the allowed_endpoints.yaml write: additions.
  6. Introduce the test-gating convention (LARK_TEST_INTEGRATION=1 + Base-đệm hard-assert + conftest.py) explicitly as net-new (not "mirror existing").
  7. Apply OQ-7 correction: orphan backups are never auto-deleted immediately — always retained + logged; deletion only via explicit audited 7-day sweep (remove PATCH1's "delete-if-safe" path).
  8. Fold OQ-2/3/4/5 confirmations (no change) for completeness.

5. Answers to required reconcile questions

  • Repo root / branch / HEAD / status: NOT DETERMINABLE (no git/exec tool here); source verified by direct file reads instead.
  • Was source actually inspected? YES — 14 live files read and verified (corrects earlier "not inspected" status).
  • Match/drift summary: 5 MATCH, 2 partial, 5 MATERIAL DRIFT (CLI, exit codes, LarkCore write/idempotency, audit 2-phase, test harness).
  • Does PATCH1 need another patch? YES — PATCH2 required (8 items above).
  • Is design commit recommended now? NO — not until PATCH2 realigns interfaces; also OQ-6 mechanics still block the commit action.
  • Can Sprint 1 start? NO — STOP. Sprint 1 is gated on PATCH2 acceptance (+ OQ-6 commit mechanics by a repo/shell-capable actor).

S177-R0 complete. STOP — material drift. Route to GPT/User. No code, no commit, no self-advance.