AI & Tech

Without Palantir: What a Contract, a Test and an Approval List Cover

Without buying Palantir, how far can one MODEL.md page, one test and one approval list protect your data? Three of the author’s public repos show what the code actually enforces, what still depends on people, and when a heavier platform or authorization engine is worth considering.

2026.09.02 · By dvdmaru · ~11 min read · 2,479 words

本文另有中文版:沒有 Palantir:一頁契約、一支測試、一份核准清單,各做到了 Ontology 的哪一格

basketball-tools has a MODEL.md with two rules in it. One, on line 28, says no row in the NBA standings may carry a division field. The other, in the hand-off section near the end of the file, says: “Acceptance test: every field-level fact in the body must point to a source field. If it can’t, it is model knowledge → cut.” (translated from the Chinese)

Once written down, does any code enforce them? The first one, yes: a test goes red if a division field turns up. The second, no. It is a rule for the person reviewing the article.

So the same document has one rule a machine enforces and one that is only written. That gap is what this piece is about. Without Palantir, what can one person do with a document, a test and an approval list, and how far does it get toward what an Ontology does?

The examples are three public repos the author maintains: basketball-tools, health-tools and racing-tools, all public under dvdmaru/ on GitHub. No platform, three static sites. The short answer: local guardrails can be self-built, and each repo has one thing its code really enforces. But a rule in a document is not a rule in execution, and what the health repo traces is a build-time source for a value, not the decision lineage a platform describes. The comparison that matters is not whether a feature exists, but who enforces what, and when.

First, scope. The four lenses used here, semantic, action, permission and lineage, are this article’s own frame for lining the repos up with Palantir’s documentation. They are not Palantir’s classification: its documentation splits things into data, logic, action and security, which Part 1 and part 2 of the series covered. The descriptions of the repos come from the files as viewed on September 2, 2026; this revision did not re-read today’s repos.

A contract was written. Who enforces it? basketball-tools

Start with how Palantir treats a definition. SuperRepo’s Ontology-as-code lets you declare object types, links and actions in TypeScript, and the documentation says those definitions “are compiled and materialized into real entities on your enrollment after your product is deployed.” The OSDK regenerates when definitions change. (Palantir’s page marks SuperRepo as beta and says it may not be available on every enrollment; the page already carried that notice when the original article was written in September.) Change the definition, and what is on the platform changes with it.

MODEL.md cannot do that. It generates nothing. The guard is tests/test_snapshot_schema.py. It keeps the table from MODEL.md §1 as a separate Python set (for example NBA_ROW_FORBIDDEN = {"division"}) and checks three things: required fields must not disappear, an unregistered field turns the test red, and NBA rows may not carry division. The test also has positive controls. It injects a fake division into a copy and confirms the checker complains, then removes a required field and confirms the same. If it finds no snapshot files at all, it fails rather than passing quietly. The test runs on PRs, on pushes and before each daily data pull.

Here is where it stops. The test checks MODEL.md itself only for existence and for three anchor strings: “資料契約”, “division” and “final_four”. It does not parse the table or compare it with the Python set. So there are two copies of the contract, the table in MODEL.md and the set in the test file. CI catches drift between the snapshots and the test set. It does not catch the MODEL.md table and the test set disagreeing with each other, and keeping those in step falls to a person in the same PR.

The test also verifies only key sets, as set differences over “required”, “allowed” and “forbidden”. It does not check value types, declare object identity or a primary key, check link types, or check how a data source maps to a property. Palantir’s documentation defines object type, property, link type, primary key and backing datasource separately, and does not say every object type must carry all five. Next to that, this test guards a small slice of property schema. And the positive controls show the test fires on the mistakes injected into it, not that the data is semantically right.

What a citation receipt proves: health-tools

The rule in health-tools’ MODEL.md is that every number on a page must point back to a row, and that row’s quote must be found by grep in the source snapshot. scripts/check-receipts.py goes through each row in data/criteria/*.json and checks four things: the doc_id exists in the manifest, the quote is not empty, the quote is found in the snapshot when the snapshot exists, and it prints SKIP when the snapshot does not.

The comparison strips only whitespace. Full-width versus half-width characters, case, and ≧ versus ≥ are left alone on purpose, because those differences are the fingerprints that send you back to the original. A miss is a FAIL. It cannot be put on an exception list, and you may not edit the quote to fit the snapshot.

The gate proves one concrete thing: for a registered criteria row, the quote is found in its snapshot. It does not prove several others. It does not work backward to check that every number on the page has a row behind it. When a snapshot is not on disk the script’s own message reads: “snapshot missing prints SKIP, not PASS. SKIP means ‘not yet verified’, and the exit code is still 0.” So CI is not blocked. And a string match after whitespace normalization is not semantic verification: finding the quote does not mean the page reads it correctly.

Now compare Palantir’s described decision lineage, in which when a decision was made, against which data version and through which application are recorded automatically. That is runtime: decision to data version to application. The health site does build-time value to source. Different layers, so one cannot stand in for the other.

The repo also has a history file where a status enum keeps unconfirmed rows from rendering, which a machine enforces. But there is no structured actor or timestamp field for who moved a row from “confirmed (needs supplement)” to “confirmed”, and for that reason it cannot be called a complete audit trail.

The gap between approval and identity: racing-tools

config/approved.json opens with: “default-deny publish approval list. Only articles whose article_sha256 matches exactly go into the output.” (translated) An approval has seven fields, but build-articles.py compares only article_sha256. facts_sha256 and check_report_sha256 are kept as reconciliation evidence, not conditions for admission.

On a mismatch, the build script prints a warning and skips the article, and that is not a build failure. Read only that layer and a mismatch looks like a quiet skip. There is another layer before it. PRs and the weekly pipeline run unit tests first, and ApprovedArticlesAreStillApproved turns the same mismatch into a test failure. The test file’s comment says it plainly: “make it a mechanical block: if the sha doesn’t match, the test goes red and the PR can’t merge.” (translated) The two layers together are the real gate.

No code manages the identity step. A comment in approved.json says the approver must differ from the author, but that sentence lives only in the comment. The build script reads only article_sha256 throughout; it never reads or compares approved_by. Errata live in a separate file, data/errata.json, maintained by hand. Approval, publishing and errata sit in three places, lined up by slug and sha256 naming conventions, with no shared type tying them into one action.

Put that next to a Palantir action. A Palantir action type arranges submission criteria, parameter validation, Ontology edits, optional side effects and an action log once configured under one definition. Not every action type has all of these; an external webhook is not guaranteed to succeed or fail together with the Ontology change. racing-tools has no Ontology edits, no machine check of who submitted, and no action log generated each time something passes or is refused. approved.json and errata.json are state files kept by hand. So the most this example supports is that, for publishing an article, there is a default-deny admission step. It does not show that general external write-back can be self-built this way.

And permissions?

Palantir’s documentation says object security policies can be set on an object type, independently of the permissions on the backing data source. In the three repos I searched a limited scope (.py, .json, .md, .yml and similar files, for permission, role, acl and auth) and found no access-control implementation. The hits were workflow permissions: settings, HTTP headers, role="img" in HTML, and strings like author.

That is a negative result within a limited search. It does not mean the repos have no permission mechanism anywhere, and it does not mean self-building one is impossible. None of the three repos connects an outside authorization engine either.

The three repos side by side

One thing the code actually enforcesWhat depends on people or is not coveredHow it differs from Palantir
basketball-toolsSnapshot fields that disagree with the test set turn it redMODEL.md table and test set synced by hand; “cut anything without a source” is a human ruleThe definition generates nothing; key sets only, no types, keys or links
health-toolsA registered criteria row’s quote must be found when its snapshot existsRows without snapshots SKIP; page numbers are not traced back; history lacks actor and timeBuild-time value to source, not runtime decision lineage
racing-toolsA mismatched article_sha256 skips the article, and tests block the PRApprover-differs-from-author is a comment; approval and errata are hand-kept state filesNo Ontology edits, no identity check, no automatic action log

When to move to something heavier

What the three repos achieve is a small guarded slice each, built from a document plus a program or a list. Whether to go further depends on a few conditions: whether several processes share the same entities, whether you need actions that write back, whether permissions or compliance are a hard requirement, whether you need to say where a number came from, and whether the data is spread across systems. The more of these are true, the more it is worth putting those slices under one runtime-enforced system.

For a read-only static site where you control the schema and need no enforced authorization, the author’s trade-off is the three slices covered here: a field-shape contract, a citation receipt and a pre-publish gate. It is a recommendation for that setting, not a security guarantee for static sites in general.

These three examples show only that the pieces each one guards can be built separately. Whether integrating them, maintaining them for years and enforcing them consistently is merely an engineering problem is something three repos do not answer.

Back to the rule from the opening: “cut anything without a source” sits in MODEL.md and no code enforces it. Which kind of rule do you have in your own docs? Next time you write a contract, sort it into three columns: who runs it, when it runs, and who is responsible for what it cannot catch.

Next in the series: The W3C’s Ontology Is for Inference; Palantir’s Defines Actions, on what the W3C, dbt, Cube, LookML and Palantir each mean by ontology or semantic layer.

FAQ

Does a one-page MODEL.md count as Palantir’s object type definition?

It is an analogy, not an equivalence. In the SuperRepo path, Ontology-as-code, TypeScript object type definitions are compiled and materialized into platform entities after deployment, and the OSDK regenerates when the definitions change. Palantir’s documentation marks SuperRepo as beta, so it may not be available on every enrollment. MODEL.md generates nothing. The test file keeps a separate Python key set, checks MODEL.md only for existence and three anchor strings, and the two copies are synced by hand in the same PR.

Can an approval list replace a Palantir action?

Not on this evidence. The racing-tools approval list guards one thing, publishing an article: only an article whose article_sha256 matches exactly goes into the output. It edits no Ontology objects, no code checks who submitted, and the publish gate does not compare the approved_by field. So the example does not show that general write-back actions can be self-built this way.

How does self-built lineage differ from Palantir’s decision lineage?

The health-tools receipt gate checks value to source at build time: for a registered criteria row whose snapshot is on disk, the quote must be found by grep; a row with no snapshot shows SKIP and does not block CI. Palantir’s documentation describes decision lineage as decision to data version to application, recording when a decision was made, against which version of the data, through which application. The two sit at different layers.

When is a heavier guardrail worth building?

The more of these hold — several processes sharing the same entities, actions that write back, permissions or compliance as a hard requirement, a need to say where a number came from, data spread across systems — the more it is worth binding the mechanisms into one runtime-enforced system. For a read-only static site where you control the schema and need no enforced authorization, the author’s trade-off is three slices: a field contract, a citation receipt and a pre-publish gate. That is a recommendation for this setting, not a security guarantee for every static site.

Sources and scope

Data as of September 2, 2026 (the original article’s date); editorially revised October 16, 2026.

  • Palantir documentation, read September 2026: SuperRepo Core concepts and SuperRepo Overview (beta notice), plus the Object types, Object security policies, Action types overview and Why Ontology pages.
  • Three repos (GitHub dvdmaru/basketball-tools, dvdmaru/health-tools, dvdmaru/racing-tools): files as viewed on September 2, 2026; the original did not record commit SHAs. Main files: MODEL.md, tests/test_snapshot_schema.py, scripts/check-receipts.py, data/criteria/history-schema.json, config/approved.json, scripts/build-articles.py, tests/test_site_facts.py, data/errata.json.