S177-R0 — Code Reconcile Report (2026-05-19)
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-clientv1.0.0 (version.txt), Python >=3.10, depsrequests>=2.28, PyYAML>=6.0, tabulate>=0.9; devpytest>=7.0. Entry pointcli.lark_tool:main(pyproject.toml [project.scripts]). lark_client/__init__.pyexportsLarkCore, 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 fromconfig/allowed_endpoints.yaml, iterates bothread:andwrite:sections (write section already supported, currently empty); rate limiter =fcntl.flockon/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)andpaginate(...).requestsimported inside core only (consistent with the "no direct requests outside core" rule).exceptions.py: baseLarkClientError; subclassesEndpointNotAllowed,CredentialPermissionLost,TokenRefreshError,LarkAPIError(status_code, code, msg),RateLimitExceeded.AuditLogger(audit.py): append-only JSONL/var/log/lark-ops/YYYYMMDD.jsonl; publiclog_call(agent, command, endpoint, method, status, duration_ms, api_calls, error, request_id)andlog_cli_invocation(...); private_write(entry)masks credential-like 30+ char strings (skipsts/agent/cmd). No planned/started (2-phase) method; fixed call-oriented schema.LarkReader:list_tables,list_fields,search_records— read-only, viacore.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_tokenraiseKeyError;list_all,list_by_role.- CLI (
cli/lark_tool.py):argparsewith nested subparsers; per-commandcmd_*functions; manual dispatch inmain(argv); exit codesEXIT_OK=0, EXIT_USER_ERROR=1, EXIT_NETWORK_API=2, EXIT_INTERNAL=3, EXIT_PERMISSION_CONFIG=4, EXIT_CRED_LOST=5; existing groupsregistry,schema,audit; submodulescli.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: key88-phai-cu-base-dem, app_tokenNf2bb1ExXaYnlksgoyQl72GNgAc, rolestaging. Production88-phai-cutokenYSIkb8PxOaNaozs2vwalOOcagkf, rolecore. (Base đệm is detectable by key ORrole=="staging".)- Tests (
tests/test_core.py): pytest; mix of live-API integration (test_token_obtainablehits real Lark, no mock, ungated) and offline whitelist tests; noconftest.py; noLARK_TEST_INTEGRATIONgating 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:
- Replace §G.3 Click design with argparse subparser design (record/field commands as
cmd_*, registered on existingsubparsers, exact arg flags). - Replace §B.3 exit-code table with the actual enum + a sanctioned extension for approval/safety/partial-failure codes.
- Correct exception names/parents: subclass
LarkClientError; useLarkAPIError; specifyUnknownBaseErrorwrappingKeyError(or useKeyError). - Specify AuditLogger extension (new 2-phase/emergency/orphan methods + entry schema + masking-safety for
idempotency_key/backup_ref). - Specify LarkCore guarded-write entrypoint + idempotency/client_token mechanism and the
allowed_endpoints.yaml write:additions. - Introduce the test-gating convention (
LARK_TEST_INTEGRATION=1+ Base-đệm hard-assert +conftest.py) explicitly as net-new (not "mirror existing"). - 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).
- 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.